require(plotly)
require(tidyverse)
require(ggridges)
require(cowplot)
require(RColorBrewer)
require(grid)
require(ggtext)
old <- theme_set(theme_bw(base_size = 16))

This is the second part of the analysis. In the first part (see ../input/PHO5-data/20231019-pool-qc-PHO5.Rmd), I did QC and exported the filtered dataset. Here, I will continue working with that dataset and answer our biological questions.

Goal

  • Analyze the full chimera set flow results for PHO5pr-mCherry reporter.
  • Develop an analysis pipeline to perform QC, correction (if needed) and plotting the results.

Data

Import the background subtracted data

dat0 <- read_tsv("../input/20231023-PHO5-bg-subtracted-data.tsv", col_types = "ccccdddddc")

Filter the data

dat <- filter(dat0, host != "PHO84", flag == "pass", date != "02/10") %>% 
  # based on previous QC, the following sample (both replicates) have high
  # variance - one biological replicate is highly expressed, while the other 
  # two have mNeon, but barely any RFP expression.
  mutate(
    host = fct_recode(host, pho2 = "pho2∆"),
    flag = ifelse(plasmid == "233" & host == "pho2", "high.var", flag))

Number of replicates left for each sample

expt <- dat %>% 
  filter(host %in% c("PHO2", "pho2"), !plasmid %in% c("188", "194")) %>% 
  group_by(date, plasmid, host) %>% 
  summarize(n = n(), .groups = "drop")

expt %>% 
  ggplot(aes(x = plasmid, y = n)) +
  geom_col(aes(fill = host)) + 
  facet_grid(date ~ .) +
  scale_fill_manual(values = c("PHO2" = "gray30", "pho2" = "gray70")) +
  theme_minimal() + background_grid(major = "none") + panel_border(size = 0.5) +
  scale_y_continuous(name = "Replicates", breaks = c(6)) + xlab(NULL) +
  theme(axis.text.x = element_text(angle = 90),
        strip.text.y = element_text(angle = 0),
        legend.position = "top")

Chimera makeup information

meta <- read_tsv("../input/20230208-chimera-Pho4-makeup.txt", col_types = "ccccc")

Summarize data

Here we would like calculate the ratio of RFP/GFP for each chimera (plasmid) across all replicates, including from different days. Note that the parameter of interest is a ratio, which can be estimated using either “means of ratios” or “ratios of means”. These are just two specific instances of a more general estimator, representing two choices of the weights. The “means of ratios” first calculates the ratios for each replicate within a plasmid, then average them. In this calculation, each replicate is given the weight of 1/n (equal). The “ratios of means” first sum up the GFP and RFP values separately across the replicates for each plasmid, then take the ratio between them. In this estimator, the weight for each replicate is x / sum(x), where x is the denominator in the ratio, i.e., GFP. In other words, this estimator will give more weights to the replicates where the chimera had a higher expression level.

Both estimators are known to be biased. We will ignore that for the moment. In terms of a choice between the two, it seems that there is no reason to give more weights to the experiments with a higher GFP signal. So, the “means of ratios” seems a more natural choice. However, we will calcultae both and decide later.

A final question is how to calculate the variance of the ratio estimate. According to the survey package manual, an approximate estimator for the variance is

\[ r = \frac{\bar{y}}{\bar{x}}, \text{where}\ \bar{y}=\frac{1}{n}\sum_{i=1}^{n}y_i\ \text{and}\ \bar{x}=\frac{1}{n}\sum_{i=1}^{n}x_i\ \\ \hat{V}(r) = (1-\frac{n}{N})(\frac{1}{\bar{x}^2})\frac{s_r^2}{n}\ \text{where}\ s_r^2=\frac{1}{n-1}\sum_{i=1}^{n}(y_i-rx_i)^2 \]

Assuming that N>>n, we can ignore the first term in the variance estimator. The rest can be calculated from the data

datsum <- dat %>%
  filter(!is.na(plasmid)) %>% 
  group_by(plasmid, host) %>% 
  summarize(
     n = n(),
    mG = mean(BL1.H),
    mR = mean(YL2.H),
     A = mean(YL2.H/BL1.H),
     r = mR/mG,
    s2 = 1/(n-1)*sum((YL2.H - r*BL1.H)^2),
    vr = 1/(mG^2)*s2/n,
    se = sqrt(vr),
    .groups = "drop"
  ) %>% 
  select(-s2, -r, -vr)# %>% 
  #pivot_wider(names_from = host, values_from = BL1.H:`nR/G`) %>% 
  #mutate(`pho2∆/PHO2` = `R/G_pho2∆`/`R/G_PHO2`,
  #       `n.pho2∆/PHO2` = `nR/G_pho2∆`/`nR/G_PHO2`)

For each chimera, we would also like to calculate three values:

  1. A in pho2∆: this is its base activity without Pho2
  2. A in PHO2: this is its full activity with Pho2
  3. A_PHO2 / A_pho2∆: this is the Pho2 enhancement of activity

We assign the chimeras into several groups, based on their A_PHO2 and A_PHO2/A_pho2∆

ximera <- datsum %>%
  pivot_wider(id_cols = plasmid, names_from = host,
              values_from = c(A, se)) %>% 
  mutate(
    rA_PHO2 = A_PHO2 / A_PHO2[plasmid == "194"],
    rA_pho2 = A_pho2 / A_pho2[plasmid == "194"],
    boost = A_PHO2 / A_pho2,
    group = case_when(
      plasmid %in% c("188", "194") ~ "ref",
      rA_PHO2 < 0.2                ~ "n.f.",
      .default = "chimera"
    ),
    group = fct_relevel(group, "ref", "chimera", "n.f.")
  ) %>% 
  left_join(select(meta, plasmid, set, symbol), by = "plasmid") %>% 
  mutate(symbol = fct_reorder(symbol, rA_PHO2, .desc = TRUE)) %>% 
  relocate(c(set, symbol, group), .after = plasmid)

Export the summarized data

```r
write_tsv(ximera, file = \../output/20231125-PHO5pr-chimera-summarized.tsv\)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


To be able to plot all the data points, let's generate another data frame with the individual ratios.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuZGF0X3NlcCA8LSBkYXQgJT4lXG4gIGZpbHRlcighaXMubmEocGxhc21pZCkpICU+JSBcbiAgbXV0YXRlKEEgPSBZTDIuSC9CTDEuSCkgJT4lIFxuICBzZWxlY3QocGxhc21pZCwgaG9zdCwgQkwxLkgsIFlMMi5ILCBBLCBmbGFnKSAlPiUgXG4gIGxlZnRfam9pbihzZWxlY3QobWV0YSwgcGxhc21pZCwgc2V0LCBzeW1ib2wpLCBieSA9IFwicGxhc21pZFwiKSMgJT4lIFxuICAjbXV0YXRlKHN5bWJvbCA9IGZjdF9yZW9yZGVyKHN5bWJvbCwgckFfUEhPMiwgLmRlc2MgPSBUUlVFKSkjICU+JSBcbmBgYCJ9 -->

```r
dat_sep <- dat %>%
  filter(!is.na(plasmid)) %>% 
  mutate(A = YL2.H/BL1.H) %>% 
  select(plasmid, host, BL1.H, YL2.H, A, flag) %>% 
  left_join(select(meta, plasmid, set, symbol), by = "plasmid")# %>% 
  #mutate(symbol = fct_reorder(symbol, rA_PHO2, .desc = TRUE))# %>% 

Analysis

Plotting functions

source("../script/20240211-chimera-data-plotting-functions.R")

—>

Source the scripts

my_plot_ratio <- function(selection){
  # custom colors for this function
  date.colors = c(brewer.pal(name="Dark2", n = 8), brewer.pal(name="Paired", n = 8))
  host.colors = c("PHO2" = "gray30", "pho2" = "gray70")
  point.colors = c("PHO2" = "forestgreen", "pho2" = "purple4")
  # prepare data
  tmp <- my_data_prep(selection)
  # plotting
  p <- tmp %>% 
    select(-c(FSC.H, nGFP, nRFP, flag)) %>% 
    mutate(`R/G` = YL2.H/BL1.H) %>% 
    pivot_longer(cols = c(BL1.H, YL2.H, `R/G`), 
                 names_to = "parameter", values_to = "value") %>% 
    mutate(parameter = factor(parameter, levels = c("R/G", "YL2.H", "BL1.H"),
                              labels = c("RFP/GFP", "PHO5pRFP", "Pho4-GFP"))) %>% 
    ggplot(aes(x = symbol, y = value, group = host)) + 
    stat_summary(aes(group = host), fun.data = "mean_cl_boot", geom = "errorbar",
                 position = position_dodge(0.5), width = 0.3) +
    geom_bar(aes(fill = host), width = 0.5, alpha = 0.8,
             stat = "summary", fun = "mean", position = position_dodge(0.5)) +
    geom_point(data = function(x) subset(x, !symbol %in% c("CCCCC", "SSSSS")),
               aes(group = host, color = date), size = 1, shape = 3, alpha = 0.9,
               position = position_jitterdodge(dodge.width = 0.5, jitter.width = 0.1)) +
    scale_color_manual(values = date.colors, guide = "none") +
    #geom_point(data = function(x) subset(x, !symbol %in% c("CCCCC", "SSSSS")),
    #           aes(group = host, color = host), size = 1, shape = 3, alpha = 0.9,
    #           position = position_jitterdodge(dodge.width = 0.5, jitter.width = 0.1)) +
    #scale_color_manual(values = point.colors) +
    scale_fill_manual(values = host.colors) +
    facet_grid(parameter~group, scales = "free", space = "free_x") +
    theme_bw(base_size = 18) + background_grid(minor = "none") + 
    xlab("Pho4 chimera") +
    theme(axis.text.x = element_text(angle = 30, hjust = 1, family = "mono"),
          legend.position = "top",
          axis.title = element_blank())
  return(p) 
}

Modify the component plotting function for special purposes

host.labels = c("PHO2", "pho2∆")
point.colors = c("PHO2" = "forestgreen", "pho2" = "purple4")
p1 <- dat %>% 
  filter(!is.na(plasmid)) %>% 
  mutate(plasmid = fct_reorder(plasmid, BL1.H, .fun = median) %>% 
           fct_relevel("194", "188")) %>% 
  ggplot(aes(x = plasmid, y = BL1.H)) +
  geom_point(aes(color = host), position = position_jitter(0.1),
             size = 1.1) + 
  scale_color_manual("Host", values = point.colors, labels = host.labels) +
  scale_y_log10(breaks = c(100, 1000, 10000), expand = expansion(mult = 0.1)) +
  scale_x_discrete(expand = expansion(mult = 0.03)) +
  xlab("Pho4 constructs") + ylab("Pho4-mNeon (a.u.)") +
  theme_cowplot() + panel_border(color = "gray30", size = 1.2) +
  theme(axis.text.x = element_text(angle = 90, size = rel(0.6), vjust = 0.5),
        axis.text.y = element_text(size = rel(0.8)),
        axis.title = element_text(size = rel(0.9)),
        axis.line = element_blank(),
        legend.position = c(0.05, 0.9),
        legend.direction = "horizontal",
        legend.text = element_text(face = 3))
Warning: A numeric `legend.position` argument in `theme()` was deprecated in ggplot2 3.5.0.
Please use the `legend.position.inside` argument of `theme()` instead.
p1

#ggsave("../img/20240307-Pho4-chimera-protein-level-variation.png", 
#       width = 6, height = 3)

Pho4 chimera protein level variation

What is the distribution of Pho4 chimera protein levels? Is the mCherry/mNeon ratio a faithful measure of the chimera’s activities?

Distribution of Pho4-mNeon levels grouped by plasmid and host.

tmp <- dat %>% 
  filter(plasmid == "194", host == "PHO2")

lm <- lm(YL2.H ~ BL1.H, data = tmp)
summary(lm)

Call:
lm(formula = YL2.H ~ BL1.H, data = tmp)

Residuals:
    Min      1Q  Median      3Q     Max 
-6194.6 -1651.7  -169.8  1417.1  6612.2 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 9562.8515  1114.3148   8.582 1.48e-14 ***
BL1.H         10.4668     0.7911  13.230  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2284 on 142 degrees of freedom
Multiple R-squared:  0.5521,    Adjusted R-squared:  0.5489 
F-statistic:   175 on 1 and 142 DF,  p-value: < 2.2e-16
p2 <- tmp %>% 
  ggplot(aes(x = BL1.H, y = YL2.H)) +
  geom_point(size = 1.5) + 
  stat_smooth(method = "lm", formula = y ~ x) +
  xlab("Pho4-mNeon") + ylab("PHO5pr-mCherry") +
  theme_cowplot() + 
  panel_border(color = "gray30", size = 1.2) +
  theme(axis.line = element_blank(),
        axis.title = element_text(size = rel(1.2))
  )

p2
ggsave("../img/20240307-ScPho4-mCherry-vs-mNeon-consistent.png", 
       width = 4, height = 3.5)

All chimera (ScPho4 and CgPho4 removed) protein levels by host

host.colors =  c("PHO2" = "gray60", "pho2" = "orange")

tmp <- dat %>% 
  filter(plasmid %in% c("188", "194"), 
         date %in% c("02/08", "02/11", "02/18", "02/21", "02/23", "03/31")) %>% 
  mutate(A = YL2.H/BL1.H,
         Pho4 = factor(plasmid, levels = c("194", "188"), 
                       labels = c("ScPho4", "CgPho4"))) 
p3 <- tmp %>% 
  ggplot(aes(x = date, y = A)) + 
  geom_bar(aes(fill = host), stat = "summary", fun = "mean", 
           position = position_dodge(0.9), alpha = 0.9) +
  geom_point(aes(group = host), size = 0.6, shape = 3,
             position = position_dodge(width = 0.9)) + 
  scale_fill_manual("Host", values = host.colors, labels = host.labels) +
  scale_x_discrete(labels = 1:6) +
  #stat_summary(fun.data = "mean_se", geom = "pointrange", color = "red") +
  facet_grid(Pho4 ~ .) +
  ylab("mCherry/mNeon") + xlab("Replicate") +
  theme_cowplot() + panel_border(color = "gray30", size = 1.2)+
  theme(axis.text = element_text(size = rel(0.7)),
        axis.title = element_text(size = rel(1)),
        axis.line = element_blank(),
        strip.background = element_blank(),
        legend.position = "top",
        legend.title = element_text(size = rel(0.9)),
        legend.text = element_text(size = rel(0.8), face = 3))
p3

#ggsave("../img/20240307-CgPho4-mCherry-vs-mNeon-consistent.png", 
#       width = 4, height = 3.2)

# sample size per day of experiment
tmp %>% count(date, Pho4, host) %>% 
  pivot_wider(names_from = host, values_from = n)
cv <- dat %>% 
  select(-nGFP, -nRFP) %>%
  pivot_longer(FSC.H:YL2.H, names_to = "parameter", values_to = "intensity") %>% 
  group_by(date, plasmid, host, parameter) %>% 
  summarize(
    n = n(),
    mean = mean(intensity),
    cv = sd(intensity)/mean(intensity),
    .groups = "drop"
  ) %>% 
  arrange(desc(cv))

High variance samples

Summarize the background subtracted data by calculating the means and cv for each strain.

control <- filter(dat, plasmid == "194", host == "PHO2") %>% 
  separate(well, into = c("row", "col"), sep = 1) %>% 
  droplevels()

Use the control strain (pH194 with PHO2) to identify and correct for systematic biases

gfp.model.0 <- lm(BL1.H ~ log10(events) + date + row*col, data = control)
step(gfp.model.0)
Start:  AIC=1507.62
BL1.H ~ log10(events) + date + row * col

                Df Sum of Sq     RSS    AIC
- row:col        6     96964 3731743 1499.4
- log10(events)  1     12751 3647531 1506.1
<none>                       3634779 1507.6
- date          11   3415318 7050097 1581.0

Step:  AIC=1499.41
BL1.H ~ log10(events) + date + row + col

                Df Sum of Sq     RSS    AIC
- row            3     92309 3824052 1496.9
- log10(events)  1      9919 3741662 1497.8
<none>                       3731743 1499.4
- col            2   1030496 4762240 1530.5
- date          11   3409784 7141528 1570.9

Step:  AIC=1496.93
BL1.H ~ log10(events) + date + col

                Df Sum of Sq     RSS    AIC
- log10(events)  1     16207 3840259 1495.5
<none>                       3824052 1496.9
- col            2   1024530 4848582 1527.1
- date          11   3423810 7247862 1567.0

Step:  AIC=1495.54
BL1.H ~ date + col

       Df Sum of Sq     RSS    AIC
<none>              3840259 1495.5
- col   2   1068825 4909084 1526.9
- date 11   3422018 7262277 1565.3

Call:
lm(formula = BL1.H ~ date + col, data = control)

Coefficients:
(Intercept)    date02/09    date02/11    date02/16    date02/18    date02/19    date02/20    date02/21    date02/22  
     1827.3       -209.7       -259.9       -351.5       -242.3       -319.0       -499.0       -343.8       -233.1  
  date02/23    date03/30    date03/31         col5         col9  
     -439.5       -533.6       -569.1       -107.4       -211.0  
gfp.model.1 <- lm(BL1.H ~ date + col, data = control)

Model for mNeon

rfp.model.0 <- lm(YL2.H ~ log10(events) + date + row*col, data = control)
step(rfp.model.0)
Start:  AIC=2229.67
YL2.H ~ log10(events) + date + row * col

                Df Sum of Sq        RSS    AIC
- row:col        6  13201086  560400308 2221.1
<none>                        547199222 2229.7
- log10(events)  1  13838563  561037785 2231.3
- date          11 852447575 1399646797 2342.9

Step:  AIC=2221.11
YL2.H ~ log10(events) + date + row + col

                Df Sum of Sq        RSS    AIC
<none>                        560400308 2221.1
- log10(events)  1  12713396  573113704 2222.3
- col            2  65908721  626309029 2233.1
- row            3 154001100  714401408 2250.1
- date          11 851423159 1411823467 2332.2

Call:
lm(formula = YL2.H ~ log10(events) + date + row + col, data = control)

Coefficients:
  (Intercept)  log10(events)      date02/09      date02/11      date02/16      date02/18      date02/19  
      19089.8         3496.2        -6513.6        -4709.4        -6221.7        -3241.9        -6452.8  
    date02/20      date02/21      date02/22      date02/23      date03/30      date03/31           rowC  
      -4558.0        -4776.0        -3995.3        -5054.2        -8507.9       -10061.8        -1882.5  
         rowE           rowG           col5           col9  
      -2429.8        -2641.7         -954.5        -1663.5  
rfp.model.1 <- lm(YL2.H ~ log10(events) + date + row + col, data = control)

Model for PHO5pr::RFP

tmp <- dat %>% 
  # remove one sample with only one valid day of experiment
  filter(!(plasmid == "218" & host == "PHO2"), !plasmid %in% c("188", "194", NA)) %>% 
  nest(data = c(date, BL1.H, YL2.H), .by = c(plasmid, host))

day.var.gfp <- tmp %>% 
  mutate(model = map(data, function(df) lm(BL1.H ~ date, data = df)),
         tidied = map(model, broom::tidy)) %>% 
  unnest(tidied) %>% 
  filter(term != "(Intercept)") %>% 
  mutate(p.adj = p.adjust(p.value, method = "BH")) %>% 
  select(-data, -model) %>% 
  filter(p.adj < 0.10) %>% 
  arrange(plasmid, host)

day.var.rfp <- tmp %>% 
  mutate(model = map(data, function(df) lm(YL2.H ~ date, data = df)),
         tidied = map(model, broom::tidy)) %>% 
  unnest(tidied) %>% 
  filter(term != "(Intercept)") %>% 
  mutate(p.adj = p.adjust(p.value, method = "BH")) %>% 
  select(-data, -model) %>% 
  filter(p.adj < 0.10) %>% 
  arrange(plasmid, host)

there are more systematic shifts in the RFP, significant for row, col, date and also # of events however, I won’t be removing these effects yet, because I’ve found that RFP/GFP ratios are pretty consistent across days. In other words, the variation in GFP and RFP may be cancelled out.

Check for each plasmid how consistent are the measurements between days

# extract ximera names
refs <- c("188","194")
# make a test set
day.var.gfp.list <- unique(day.var.gfp$plasmid)
day.var.rfp.list <- unique(day.var.rfp$plasmid)
p <- my_plot_ratio(c(refs,day.var.gfp.list))# + 
Warning: There was 1 warning in `mutate()`.
ℹ In argument: `host = fct_recode(host, pho2 = "pho2∆")`.
Caused by warning:
! Unknown levels in `f`: pho2∆
p

High day-to-day GFP variance: 212, 222, 229, 231, 241, 251, 252, 277, 301, 326, 328, 329, 331, 334 High day-to-day RFP variance: 212, 216, 239, 241

Plotting components for chimeras with high day-to-day variance in Pho4-mNeon

p <- my_plot_ratio(c(refs,day.var.rfp.list))
Warning: There was 1 warning in `mutate()`.
ℹ In argument: `host = fct_recode(host, pho2 = "pho2∆")`.
Caused by warning:
! Unknown levels in `f`: pho2∆
p

Watch out for CSCscC, SCCsS, SCCsS

Plotting components for chimeras with high day-to-day variance in PHO5pr-mCherry

my_scatter_plot_fix <- function(){
  # this function is the same as the one in the script file, but is used to 
  # plot region 4 effects alone, and doesn't take any input
  s1 = my_data_select(pattern = "XXXCCX")
  s2 = my_data_select(pattern = "XXXSSX")
  scatter.colors = c("ScPho4" = "forestgreen", "CgPho4" = "blue3", 
                     "P2ID:Cg" = "deepskyblue", "P2ID:Sc" = "palegreen2",
                     "P2ID:mixed" = "gray20")
  scatter.size = c("ScPho4" = 3.5, "CgPho4" = 3.5,
                     "P2ID:Cg" = 2.5, "P2ID:Sc" = 2.5, "P2ID:mixed" = 2.5)
  p <- ximera %>% 
    # exclude the alternative break point sets "A" and "B"
    # in particular, pH294 is an alternative break point CCCcs, where P2ID:Cg
    # extends to aa 270 instead of 458. it has nearly the same activities as
    # CgPho4. However, the CCCCS in the main set has significantly reduced
    # activities both with and without Pho4. We later tested whether the 
    # additional P2ID:Cg4 rescues the effect (see Cg4ext below) and it didn't
    # so far, this seems to be an one-off. we need to further investigate its
    # activities.
    filter(set %in% c("M", "S")) %>% 
    mutate(A_PHO2 = signif(A_PHO2, digits = 2),
           A_pho2 = signif(A_pho2, digits = 2),
           group = case_when(
             symbol == "CCCCC" ~ "CgPho4",
             symbol == "SSSSS" ~ "ScPho4",
             plasmid %in% s1 ~ "P2ID:Cg",
             plasmid %in% s2 ~ "P2ID:Sc",
             .default = "P2ID:mixed"
           ),
           group = fct_relevel(group, names(scatter.colors))) %>% 
    ggplot(aes(x = A_PHO2, y = A_pho2, label = symbol)) + 
    geom_abline(slope = 1) +
    geom_point(aes(color = group, size = group)) + 
    scale_color_manual(NULL, values = scatter.colors) +
    scale_size_manual(values = scatter.size, guide = "none") +
    labs(x = bquote(A[PHO2]), y = bquote(A[pho2*Delta])) +
    theme_cowplot() + panel_border(color = "gray30", size = 1.2) +
    theme(legend.text = element_text(size = rel(0.8)),
          legend.position = c(0.03, 0.83),
          axis.title = element_text(face = 2, size = rel(1.2)),
          axis.line = element_blank())
  return(p)
}

most of the day-to-day variance are canceled out after RFP/GFP normalization

All chimera, scatter plot

Plot all chimeras, coloring based on P2ID source

p <- my_scatter_plot_fix()
ggsave(filename = "../img/20240308-all-chimera-scatter-color-by-P2ID.png",
       plot = p, width = 4.5, height = 4, dpi = 300)
ggplotly(p + labs(x = "A<sub>PHO2</sub>", y = "A<sub>pho2</sub>") +
           theme_gray(base_size = 16) +
           theme(legend.text = element_markdown()), 
         tooltip = c("label", "x", "y"))
my_scatter_plot_all <- function(){
  # this function is the same as my_scatter_plot_fix except that it plots all the chimeras
  # without coloring them differently. for figure 5
  s1 = my_data_select(pattern = "XXXCCX")
  s2 = my_data_select(pattern = "XXXSSX")
  scatter.colors = c("ScPho4" = "forestgreen", "CgPho4" = "blue3", 
                     "P2ID:Cg" = "gray20", "P2ID:Sc" = "gray20",
                     "P2ID:mixed" = "gray20")
  scatter.size = c("ScPho4" = 3.5, "CgPho4" = 3.5,
                   "P2ID:Cg" = 2.5, "P2ID:Sc" = 2.5, "P2ID:mixed" = 2.5)
  p <- ximera %>% 
    filter(set %in% c("M", "S")) %>% 
    mutate(A_PHO2 = signif(A_PHO2, digits = 2),
           A_pho2 = signif(A_pho2, digits = 2),
           group = case_when(
             symbol == "CCCCC" ~ "CgPho4",
             symbol == "SSSSS" ~ "ScPho4",
             plasmid %in% s1 ~ "P2ID:Cg",
             plasmid %in% s2 ~ "P2ID:Sc",
             .default = "P2ID:mixed"
           ),
           group = fct_relevel(group, names(scatter.colors))) %>% 
    ggplot(aes(x = A_PHO2, y = A_pho2, label = symbol)) + 
    geom_abline(slope = 1) +
    geom_point(aes(color = group, size = group)) + 
    scale_color_manual(NULL, values = scatter.colors) +
    scale_size_manual(values = scatter.size, guide = "none") +
    labs(x = bquote(A[PHO2]), y = bquote(A[pho2])) +
    theme_cowplot() + panel_border(color = "gray30", size = 1.2) +
    theme(legend.text = element_text(size = rel(0.8)),
          legend.position = "none",
          axis.title = element_text(face = 2, size = rel(1.2)),
          axis.line = element_blank())

  return(p)
}

this function is the same as my_scatter_plot_fix except that it plots all the chimeras without coloring them differently. for figure 5

Plot all chimeras, for Fig. 5

my_plot_subset_ximera <- function(symbols){
  # this function plots a subset of the chimeras as horizontal bar plots
  # showing the Rel. A_PHO2 and %A_pho2∆ values
  # it takes as input a vector containing the symbols for the chimeras for 
  # plotting. the order in the vector determines the plot order
  # the endogenous ScPho4 and CgPho4 are implied
  missing <- setdiff(symbols, ximera$symbol)
  if(length(missing) != 0)
    stop(paste(missing, "are not found", sep = " "))
  
  tmp <- filter(ximera, symbol %in% c("SSSSS", "CCCCC", symbols)) %>% 
    mutate(
      rSE_PHO2 = se_PHO2 / A_PHO2[symbol == "SSSSS"],
      rSE_pho2 = se_pho2 / A_pho2[symbol == "SSSSS"]
    ) %>% 
    pivot_longer(cols = c(rA_PHO2, rA_pho2, rSE_PHO2, rSE_pho2), 
                 #pivot_longer(cols = c(A_PHO2, A_pho2, se_PHO2, se_pho2), 
                 names_to = c(".value", "parameter"), names_sep = "_",
                 values_to = "value") %>% 
    mutate(parameter = fct_relevel(parameter, "PHO2"),
           symbol = factor(symbol, levels = 
                             unique(c("SSSSS", "CCCCC", symbols)))) %>% 
    select(-c(A_PHO2:boost))
  
  # labeller
  par.explain <- c(
    PHO2 = "Rel. A<sub>PHO2</sub>",
    #boost = "Boost",
    pho2 = "Rel. A<sub>pho2∆</sub>"
  )
  
  p <- ggplot(tmp, aes(y = symbol, x = rA)) +
    geom_col(width = 0.5, color = "black", fill = "gray80") +
    geom_vline(xintercept = 1, linetype = 2, color = "gray30") +
    geom_errorbar(aes(xmin = rA - rSE, xmax = rA + rSE), width = 0.2) +
    facet_wrap(~parameter, scales = "free_x",# switch = "x",
              labeller = labeller(parameter = par.explain)) +
    scale_y_discrete(limits = rev) + 
    scale_x_continuous(expand = expansion(mult = c(0.02, 0.05))) +
    theme_cowplot() + panel_border(color = "gray30") +
    background_grid(major = "y", minor = "none") +
    theme(axis.text.y = element_text(family = "courier"),
          axis.title = element_blank(),
          axis.line = element_blank(),
          strip.placement = "outside",
          strip.background = element_blank(),
          strip.text = element_markdown())
  return(p)
}

Spotlight individual chimeras

The goal here is to plot individual chimeras in order to test specific hypotheses and make certain points.

  1. We separately tested and found that CgPho4 DBD binds the consensus DNA more strongly than ScPho4 does, and it also has two additional activation booster regions, which enhance the activity of the main AD. We therefore hypothesize that by replacing the corresponding regions in ScPho4 with the parts from CgPho4, we would create a chimeric TF that is not or far less dependent on Pho2.
  2. We also expect that those regions additively contribute to the reduced Pho2-dependence, shown as increased TF activity of the chimera in the pho2∆ background.

Design plot

my_plot_subset_ximera_alt <- function(symbols){
  # this function plots a subset of the chimeras as horizontal bar plots
  # showing the Rel. A_PHO2 and %A_pho2∆ values
  # it takes as input a vector containing the symbols for the chimeras for 
  # plotting. the order in the vector determines the plot order
  # the endogenous ScPho4 and CgPho4 are implied
  missing <- setdiff(symbols, ximera$symbol)
  if(length(missing) != 0)
    stop(paste(missing, "are not found", sep = " "))
  
  tmp <- filter(dat_sep, symbol %in% c("SSSSS", "CCCCC", symbols)) %>% 
    mutate(host = fct_relevel(host, "PHO2"),
           symbol = factor(symbol, levels = 
                             unique(c("SSSSS", "CCCCC", symbols))))

  tmp %>% count(symbol, host) %>% print()
  # labeller
  par.explain <- c(
    PHO2 = "A<sub>PHO2</sub>",
    #boost = "Boost",
    pho2 = "A<sub>pho2∆</sub>"
  )
  
  p <- ggplot(tmp, aes(y = symbol, x = A)) +
    geom_bar(stat = "summary", fun = "mean", 
             width = 0.5, color = "black", fill = "gray80") +
    stat_summary(fun.data = "mean_cl_boot", geom = "linerange",
                 color = "steelblue4") +
    geom_point(data = filter(tmp, !symbol %in% c("CCCCC", "SSSSS")), 
               size = 0.6, shape = 3, color = "gray30") +
    #geom_vline(xintercept = 1, linetype = 2, color = "gray30") +
    #geom_errorbar(aes(xmin = rA - rSE, xmax = rA + rSE), width = 0.2) +
    facet_wrap(~host, scales = "free_x",# switch = "x",
              labeller = labeller(host = par.explain)) +
    scale_y_discrete(limits = rev) + 
    scale_x_continuous(expand = expansion(mult = c(0.02, 0.05))) +
    theme_cowplot() + panel_border(color = "gray30") +
    background_grid(major = "y", minor = "none") +
    theme(axis.text.y = element_text(family = "courier"),
          axis.title = element_blank(),
          axis.line = element_blank(),
          strip.placement = "outside",
          strip.background = element_blank(),
          strip.text = element_markdown())
  return(list(data = tmp, plot = p))
}

Update 2024-11-22

Alternative design, with individual points and not relative to ScPho4. Also, individual datapoints were plotted for samples with <10 replicates

selected <- as.character(
  expression(CSSSS, SCSSS, CCSSS, SSCSS, CSCSS, SCCSS, CCCSS, SSSSC, SSCSC, CSCSC, CSScC))
#selected <- filter(meta, symbol %in% selected) %>% pull(plasmid)
my_plot_subset_ximera(selected)
ggsave("../img/20240308-selected-chimera-rel-activity.png", width = 4, height = 4)

# plot with absolute A not relative, and plot individual data points
plot.sub1 <- my_plot_subset_ximera_alt(selected)
print(plot.sub1$plot)
ggsave("../img/20241122-selected-chimera-rel-activity.png", width = 4, height = 4)

# save the data for publication
plot.sub1$data %>% 
  select(plasmid_id = plasmid, chimera_makeup = symbol, host, A) %>% 
  write_tsv("../output/20250213-Fig-5C-data.tsv")

Minimal CgPho4 parts for A_pho2

The chimera with the least amount of CgPho4 and yet have appreciable activity in the absence of Pho2 is These include SSSSS, CCCCC, SSSSC, CSSSS, SSCSS, CSCSS, CSSSC, CSCSC

# select the chimeras
selected <- as.character(
  expression(SSSSS, CSSSS, SCSSS, SSCSS, CCSSS, CSCSS, SCCSS, CCCSS)
)

# extract the data
tmp <- ximera %>% 
  filter(symbol %in% selected, set == "M") %>% 
  select(plasmid, symbol, group) %>% 
  inner_join(dat, by = "plasmid") %>% 
  mutate( `R/G` = YL2.H / BL1.H ) %>%
  filter(flag == "pass") %>% 
  select(-nRFP, -nGFP, -well, -flag)

# prepare the factor levels
split <- c(1,1,1,2); names(split) <- c("R1", "AD", "NLS")

tmp <- tmp %>% 
  separate_wider_position(symbol, split) %>% 
  mutate(across(R1:NLS, ~factor(.x, levels = c("S", "C"))))

# test A_PHO2
print("Testing A_PHO2")
[1] "Testing A_PHO2"
lm.res <- tmp %>% 
  filter(host == "PHO2") %>% 
  lm(`R/G` ~ (R1*AD*NLS), data = .) %>% 
  summary()
# adding adjusted P-value
lm.res$coefficients <- cbind(
  coef(lm.res),
  "P.adj" = p.adjust(coef(lm.res)[,'Pr(>|t|)'], method = "holm")
)
print(lm.res)

Call:
lm(formula = `R/G` ~ (R1 * AD * NLS), data = .)

Residuals:
    Min      1Q  Median      3Q     Max 
-6.4865 -1.6014 -0.5069  1.0472 12.7647 

Coefficients:
                Estimate  Std. Error     t value    Pr(>|t|) P.adj
(Intercept)    1.764e+01   2.391e-01   7.375e+01  5.546e-137 0.000
R1C            3.591e+00   1.196e+00   3.004e+00   3.046e-03 0.012
ADC            2.237e+00   1.196e+00   1.871e+00   6.296e-02 0.063
NLSC           1.499e+01   1.196e+00   1.254e+01   2.350e-26 0.000
R1C:ADC        7.578e+00   2.109e+00   3.593e+00   4.213e-04 0.002
R1C:NLSC      -5.110e+00   1.904e+00  -2.684e+00   7.959e-03 0.021
ADC:NLSC       5.586e+00   2.043e+00   2.734e+00   6.873e-03 0.021
R1C:ADC:NLSC  -1.383e+01   3.064e+00  -4.514e+00   1.142e-05 0.000

Residual standard error: 2.869 on 181 degrees of freedom
Multiple R-squared:  0.825, Adjusted R-squared:  0.8182 
F-statistic: 121.9 on 7 and 181 DF,  p-value: < 2.2e-16
# store the test results for plotting
res.PHO2 <- coef(lm.res)[-1,] %>% as_tibble(rownames = "component")

# test A_pho2
print("Testing A_pho2∆")
[1] "Testing A_pho2∆"
lm.res <- tmp %>% 
  filter(host == "pho2") %>% 
  lm(`R/G` ~ (R1*AD*NLS), data = .) %>% 
  summary()
# adding adjusted P-value
lm.res$coefficients <- cbind(
  coef(lm.res),
  "P.adj" = p.adjust(coef(lm.res)[,'Pr(>|t|)'], method = "holm")
)
print(lm.res)

Call:
lm(formula = `R/G` ~ (R1 * AD * NLS), data = .)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.63622 -0.21548 -0.01744  0.10489  1.15930 

Coefficients:
               Estimate Std. Error    t value   Pr(>|t|) P.adj
(Intercept)   1.072e+00  5.581e-02  1.920e+01  1.312e-29  0.00
R1C           6.167e-01  1.477e-01  4.176e+00  8.404e-05  0.00
ADC          -1.153e-02  1.477e-01 -7.805e-02  9.380e-01  1.00
NLSC          1.819e+00  1.477e-01  1.232e+01  3.389e-19  0.00
R1C:ADC       5.923e-01  2.433e-01  2.435e+00  1.746e-02  0.07
R1C:NLSC      1.479e+00  2.433e-01  6.081e+00  5.623e-08  0.00
ADC:NLSC      1.575e-01  2.433e-01  6.475e-01  5.194e-01  1.00
R1C:ADC:NLSC -3.795e-01  3.660e-01 -1.037e+00  3.033e-01  0.91

Residual standard error: 0.3349 on 70 degrees of freedom
Multiple R-squared:  0.9553,    Adjusted R-squared:  0.9509 
F-statistic: 213.9 on 7 and 70 DF,  p-value: < 2.2e-16
# store the test results for plotting
res.pho2 <- coef(lm.res)[-1,] %>% as_tibble(rownames = "component")

# combine the results
test.res <- bind_rows(
  "A_PHO2" = res.PHO2, "A_pho2" = res.pho2, .id = "parameter"
)

# save the output in a text file for paper
write_tsv(test.res, file = "../output/20250213-region-1-3-linear-model-test.txt")

Region 1-3 main effects and interactions

par.explain <- c(
  A_PHO2 = "A<sub>PHO2</sub>",
  A_pho2 = "A<sub>pho2∆</sub>"
)

p <- test.res %>% 
  rename(estimate = Estimate, se = `Std. Error`) %>% 
  mutate(
    component = gsub("C", "", component) %>% fct_inorder(),
    parameter = factor(parameter, levels = c("A_PHO2", "A_pho2")),
    sig = P.adj < 0.05
  ) %>% 
  ggplot(aes(x = component, y = estimate)) +
  geom_hline(yintercept = 0, linetype = 1, color = "gray50") +
  geom_col(aes(fill = P.adj < 0.05), width = 0.5, color = "black") +
  geom_pointrange(aes(ymin = estimate-se, ymax = estimate+se), size = 0.2) +
  facet_wrap(~parameter, scales = "free_y", nrow = 2,
             labeller = labeller(parameter = par.explain)) +
  scale_x_discrete() + 
  scale_y_continuous() +
  scale_fill_manual(NULL, 
                    values = c("gray90", "gray50")) +
  theme_cowplot() + panel_border(color = "gray30") +
  background_grid(major = "y", minor = "y") +
  theme(axis.text.x = element_text(angle = 90, hjust = 1, size = rel(1)),
        axis.title = element_blank(),
        axis.line = element_blank(),
        legend.position = "bottom",
        strip.placement = "outside",
        strip.background = element_blank(),
        strip.text = element_markdown(size = rel(1)))
p
ggsave("../img/20240404-region1-3-epistasis-plot.png", width = 3.5, height = 4.5)

Plot the result

selected <- as.character(
  expression(CCCSC, CCCcsC, CCCscC, CSSCC, CSSSC, CSScsC, CSSscC))
#selected <- filter(meta, symbol %in% selected) %>% pull(plasmid)
plot.sub2 <- my_plot_subset_ximera_alt(selected)
plot.sub2$plot + scale_x_continuous(expand = expansion(mult = c(0.02, 0.15)))
Scale for x is already present.
Adding another scale for x, which will replace the existing scale.
ggsave("../img/20241122-P2ID-split-rel-activity.png", width = 3.5, height = 3.5)

plot.sub2$data %>% 
  select(plasmid_id = plasmid, chimera_makeup = symbol, host, A) %>% 
  write_tsv("../output/20250213-Fig-6B-data.tsv")

Region 4 splits

We have so far focused on the main set with the 5 region design. In the scatter plot below, we see that there is a subset of chimeras in between the P2ID:Sc and P2ID:Cg ones. They are interesting in that their A_pho2∆/A_PHO2 ratios are intermediate.

scatter
scatter
# select the chimeras
selected <- as.character(
  expression(CCCCC, CCCSC, CCCcsC, CCCscC)
)

# extract the data
tmp <- ximera %>% 
  filter(symbol %in% selected) %>% 
  select(plasmid, symbol, group) %>% 
  inner_join(dat, by = "plasmid") %>% 
  mutate( `R/G` = YL2.H / BL1.H ) %>%
  filter(flag == "pass") %>% 
  select(-nRFP, -nGFP, -well, -flag) %>% 
  mutate(symbol = factor(symbol, levels = !!selected))

# test A_PHO2
print("Testing A_PHO2")
[1] "Testing A_PHO2"
lm.res <- tmp %>% 
  filter(host == "PHO2") %>% 
  lm(`R/G` ~ symbol, data = .) %>% 
  summary()
# adding adjusted P-value
lm.res$coefficients <- cbind(
  coef(lm.res),
  "P.adj" = p.adjust(coef(lm.res)[,'Pr(>|t|)'], method = "holm")
)
print(lm.res)

Call:
lm(formula = `R/G` ~ symbol, data = .)

Residuals:
    Min      1Q  Median      3Q     Max 
-5.2250 -1.0489 -0.0647  1.2813  6.6290 

Coefficients:
               Estimate Std. Error    t value   Pr(>|t|) P.adj
(Intercept)   1.809e+01  3.445e-01  5.252e+01  2.067e-45     0
symbolCCCSC   4.207e+00  9.115e-01  4.615e+00  2.766e-05     0
symbolCCCcsC -7.086e+00  9.115e-01 -7.774e+00  3.722e-10     0
symbolCCCscC  1.362e+01  9.115e-01  1.494e+01  4.511e-20     0

Residual standard error: 2.067 on 50 degrees of freedom
Multiple R-squared:  0.8711,    Adjusted R-squared:  0.8633 
F-statistic: 112.6 on 3 and 50 DF,  p-value: < 2.2e-16
# store the test results for plotting
#res.PHO2 <- coef(lm.res)[-1,] %>% as_tibble(rownames = "component")

# test A_pho2
print("Testing A_pho2∆")
[1] "Testing A_pho2∆"
lm.res <- tmp %>% 
  filter(host == "pho2") %>% 
  lm(`R/G` ~ symbol, data = .) %>% 
  summary()
# adding adjusted P-value
lm.res$coefficients <- cbind(
  coef(lm.res),
  "P.adj" = p.adjust(coef(lm.res)[,'Pr(>|t|)'], method = "holm")
)
print(lm.res)

Call:
lm(formula = `R/G` ~ symbol, data = .)

Residuals:
    Min      1Q  Median      3Q     Max 
-5.1275 -0.5912 -0.0824  0.5645  5.2165 

Coefficients:
               Estimate Std. Error    t value   Pr(>|t|) P.adj
(Intercept)   1.674e+01  2.869e-01  5.834e+01  1.176e-47 0.000
symbolCCCSC  -1.195e+01  7.591e-01 -1.574e+01  5.226e-21 0.000
symbolCCCcsC -1.255e+01  7.591e-01 -1.654e+01  6.456e-22 0.000
symbolCCCscC  1.928e+00  7.591e-01  2.540e+00  1.424e-02 0.014

Residual standard error: 1.721 on 50 degrees of freedom
Multiple R-squared:  0.9093,    Adjusted R-squared:  0.9038 
F-statistic:   167 on 3 and 50 DF,  p-value: < 2.2e-16

Statistical tests for group 1

# select the chimeras
selected <- as.character(
  expression( CSSCC, CSSSC, CSScsC, CSSscC )
)

# extract the data
tmp <- ximera %>% 
  filter(symbol %in% selected) %>% 
  select(plasmid, symbol, group) %>% 
  inner_join(dat, by = "plasmid") %>% 
  mutate( `R/G` = YL2.H / BL1.H ) %>%
  filter(flag == "pass") %>% 
  select(-nRFP, -nGFP, -well, -flag) %>% 
  mutate(symbol = factor(symbol, levels = !!selected))

# test A_PHO2
print("Testing A_PHO2")
[1] "Testing A_PHO2"
lm.res <- tmp %>% 
  filter(host == "PHO2") %>% 
  lm(`R/G` ~ symbol, data = .) %>% 
  summary()
# adding adjusted P-value
lm.res$coefficients <- cbind(
  coef(lm.res),
  "P.adj" = p.adjust(coef(lm.res)[,'Pr(>|t|)'], method = "holm")
)
print(lm.res)

Call:
lm(formula = `R/G` ~ symbol, data = .)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.4560 -0.5974 -0.1128  0.4629  4.4143 

Coefficients:
              Estimate Std. Error   t value  Pr(>|t|) P.adj
(Intercept)  6.270e+00  7.393e-01 8.481e+00 4.673e-08 0.000
symbolCSSSC  8.962e+00  1.046e+00 8.572e+00 3.949e-08 0.000
symbolCSScsC 2.558e+00  1.046e+00 2.447e+00 2.378e-02 0.024
symbolCSSscC 6.372e+00  1.046e+00 6.094e+00 5.891e-06 0.000

Residual standard error: 1.811 on 20 degrees of freedom
Multiple R-squared:  0.8127,    Adjusted R-squared:  0.7846 
F-statistic: 28.93 on 3 and 20 DF,  p-value: 1.791e-07
# store the test results for plotting
#res.PHO2 <- coef(lm.res)[-1,] %>% as_tibble(rownames = "component")

# test A_pho2
print("Testing A_pho2∆")
[1] "Testing A_pho2∆"
lm.res <- tmp %>% 
  filter(host == "pho2") %>% 
  lm(`R/G` ~ symbol, data = .) %>% 
  summary()
# adding adjusted P-value
lm.res$coefficients <- cbind(
  coef(lm.res),
  "P.adj" = p.adjust(coef(lm.res)[,'Pr(>|t|)'], method = "holm")
)
print(lm.res)

Call:
lm(formula = `R/G` ~ symbol, data = .)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.77040 -0.08856 -0.03391  0.16099  0.37316 

Coefficients:
               Estimate Std. Error    t value   Pr(>|t|) P.adj
(Intercept)   5.244e+00  1.082e-01  4.846e+01  3.257e-22 0.000
symbolCSSSC  -2.822e+00  1.530e-01 -1.844e+01  5.058e-14 0.000
symbolCSScsC -3.061e+00  1.530e-01 -2.000e+01  1.076e-14 0.000
symbolCSSscC -1.243e-01  1.530e-01 -8.125e-01  4.261e-01 0.426

Residual standard error: 0.265 on 20 degrees of freedom
Multiple R-squared:  0.9726,    Adjusted R-squared:  0.9685 
F-statistic: 237.1 on 3 and 20 DF,  p-value: 8.565e-16

Statistical tests for group 2

split <- c(1,1,1,1,1); names(split) <- paste0("P", 1:5)
tmp <- ximera %>% 
  filter(set == "M", group != "n.f.") %>% 
  separate_wider_position(symbol, split) %>% 
  mutate(across(P1:P5, ~factor(.x, levels = c("S", "C"))))
lm <- lm(A_pho2 ~ (P1+P2+P3+P4+P5), data = tmp)
summary(lm)

Call:
lm(formula = A_pho2 ~ (P1 + P2 + P3 + P4 + P5), data = tmp)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.5062 -1.1430 -0.0082  0.8638  6.5285 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -0.7264     1.0811  -0.672   0.5102    
P1C           2.3316     0.8687   2.684   0.0152 *  
P2C           0.7880     0.8687   0.907   0.3763    
P3C           2.5260     0.9010   2.804   0.0117 *  
P4C           4.0647     0.9792   4.151   0.0006 ***
P5C           1.2254     0.9010   1.360   0.1906    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2.086 on 18 degrees of freedom
Multiple R-squared:  0.7398,    Adjusted R-squared:  0.6675 
F-statistic: 10.23 on 5 and 18 DF,  p-value: 9.054e-05

Region main effect

```r
my_calc_region_effect <- function(region, variable){
  # this function takes the name of a variable of interest
  # x specifies the foreground region, which will be examined for its effect on
  # the variable of interest.
  # it then transforms the ximera data frame to preserve only the variable of
  # interest, pivots it wider after grouping by the background composition.
  
  # prepare the data by mutating the symbol column into fg and bg
  valid.var <- c(\A_PHO2\, \A_pho2\, \rA_PHO2\, \rA_pho2\, \boost\)
  if(!variable %in% valid.var)
    stop(paste0(\Please specify one of the valid variable names:\, 
                paste(valid.var, collapse = \

The main effects were calculated by averaging over all chimeras with CgPho4 region at the respective position. I’d like to break them down by backgrounds. For example, for region 3, I’d like to see the pairwise comparisons between CCCSS and CCSSS, where only region 3 differs. The steps are

  1. select the region to be compared. split the symbol into two parts - the genotype of the focal region and the rest
  2. group by the second part (rest) and calculate the differential
```r
my_plot_region_effect_twovar_line_par <- function(regions){
  # this function uses my_comp_region_effect to generate the data
  # and plot the difference in A_PHO2 and A_pho2 between the CgPho4 vs ScPho4
  # in the focal region
  dat <- map_dfr(regions, \(region) my_comp_region_effect(region), .id = \region\) %>% 
    pivot_longer(cols = c(dA_PHO2, dA_pho2), 
                 names_to = \host\, values_to = \diff\) %>% 
    mutate(host = fct_recode(host, `PHO2` = \dA_PHO2\, `pho2∆` = \dA_pho2\),
           host = fct_relevel(host, \PHO2\))
  # specify grouping variable
  dat <- mutate(dat, 
                grp = str_sub(bg, 4, 4) %>% toupper(),
                grp = fct_recode(grp, CgPho4 = \C\, ScPho4 = \S\))#,
                #sh = str_sub(bg, 5, 5) %>% toupper(),
                #sh = fct_recode(sh, CgPho4 = \C\, ScPho4 = \S\) )
  # specify arrow annotation
  arrow.x = 0.7
  arrow.y = (max(dat$diff) - min(dat$diff)) / 5 
  # plot
  p <- dat %>% 
    ggplot(aes(x = host, y = diff, label = bg)) +
    geom_point(aes(color = grp), size = 2, alpha = 0.8,
               position = position_jitter(0.1)) + 
    geom_line(aes(group = bg), linewidth = 0.2, alpha = 0.8) +
    facet_grid(region ~ grp, labeller = labeller(
      grp = c(CgPho4 = \P2ID:Cg\, ScPho4 = \P2ID:Sc\),
      region = label_both
    )) +
    scale_color_manual(\P2ID:\, values = c(\orange\, \gray30\), guide = \none\) +
    #scale_shape_manual(\DBD:\, values = c(19, 1)) +
    ylab(\Region swap effect (Cg-Sc)\) +
    theme_bw(base_size = 18) + 
    theme(
      axis.title.x = element_blank(),
      axis.title.y = element_text(size = rel(0.9)),
      axis.text.x = element_text(face = 3),
      axis.text.y = element_text(size = rel(0.8)),
      legend.text = element_text(size = rel(0.8)),
      legend.title = element_text(size = rel(0.9)),
      legend.position = \top\,
      strip.background = element_blank()
    )
  return(p)
}

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->

x = 5 p1 <- my_plot_region_effect_onevar(x, “A_PHO2”) p2 <- my_plot_region_effect_onevar(x, “A_pho2”) subplot(p1, p2, margin = 0.05) %>% layout(title = paste(“Region”, x, “swap effect on A_PHO2 and A_pho2”, sep = ” “), xaxis = list(title = paste0(”Region “, x,” from CgPho4”)), yaxis = list(title = paste0(“Region”, x, ” from ScPho4”)) )


Here, I'd like to take what I build above and create a new tibble, in which each row is a different background (makeup of the chimera except for the focal region). The value columns are:

1.  dA_PHO2 = A_PHO2_Cg - A_PHO2_Sc
2.  dA_pho2 = A_pho2_Cg - A_pho2_Sc
3.  A_PHO2_Sc = A_PHO2_Sc

The goal is to plot dA_PHO2 and dA_pho2 side-by-side for each background.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxubXlfcGxvdF9yZWdpb25fZWZmZWN0X3R3b3Zhcl9saW5lX3BhcihjKDEsMykpXG5nZ3NhdmUoXFwuLi9pbWcvMjAyNDAzMTAtcmVnaW9uLXN3YXAtZWZmZWN0LTFuMy1vbi00LnBuZ1xcLFxuICAgICAgIHdpZHRoID0gNSwgaGVpZ2h0ID0gMy41KVxuYGBgXG5gYGAifQ== -->

```r
```r
my_plot_region_effect_twovar_line_par(c(1,3))
ggsave(\../img/20240310-region-swap-effect-1n3-on-4.png\,
       width = 5, height = 3.5)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->



<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxueCA8LSBteV9kYXRhX3NlbGVjdChwYXR0ZXJuID0gXFxYWFhYQ1NcXCwgU2V0ID0gXFxNXFwpXG5teV9kYXRhX3ByZXAoeCkgJT4lIFxuICBtdXRhdGUoZ3JvdXAgPSBmY3RfcmVjb2RlKGdyb3VwLCBcXGNoaW1lcmFcXCA9IFxcbi5mLlxcKSkgJT4lIFxuICBteV9wbG90X2NvbXBvbmVudHMoKVxuZ2dzYXZlKFxcLi4vaW1nLzIwMjQwMjEzLVAySURfQ2ctREJEX1NjLWNvbXBvbmVudHMucG5nXFwsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNSlcbmBgYFxuYGBgIn0= -->

```r
```r
x <- my_data_select(pattern = \XXXXCS\, Set = \M\)
my_data_prep(x) %>% 
  mutate(group = fct_recode(group, \chimera\ = \n.f.\)) %>% 
  my_plot_components()
ggsave(\../img/20240213-P2ID_Cg-DBD_Sc-components.png\, width = 8, height = 5)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->

my_plot_region_effect_twovar_line(“4”, “5”)# %>% ggplotly() ggsave(“../img/20231221-region-swap-effect-4-on-5.png”, width = 6, height = 4, dpi = 150) my_plot_region_effect_twovar_line(“5”, “4”)# %>% ggplotly() ggsave(“../img/20231224-region-swap-effect-5-on-4.png”, width = 6, height = 4, dpi = 200)


The main plotting functions are now in a separate script file in `../script`. The plotting function below is to adapt the plot for a figure in the paper, simultaneously showing regions 1-3.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxubXlfdXBwZXJfdHJpYW5ndWxhcl9tYXQgPC0gZnVuY3Rpb24oYWx0ID0gXFxDXFwsIHZhciA9IFxcQV9QSE8yXFwsIG5mLmFzLm5hID0gRil7XG4gICMgZ2l2ZW4gdGhlIGFsdGVybmF0aXZlIGFsbGVsZSAoQy9TKSBhbmQgYSB2YXJpYWJsZSBvZiBpbnRlcmVzdCwgZS5nLiwgQV9QSE8yLFxuICAjIG91dHB1dCBhbiB1cHBlciB0cmlhbmd1bGFyIG1hdHJpeCBjb250YWluaW5nIHRoZSB2YWx1ZXMgZnJvbSB0aGUgdmFyaWFibGUgXG4gICMgb2YgaW50ZXJlc3QsIHdpdGggdGhlIHJvdyBhbmQgY29sIG51bWJlcnMgYmFzZWQgb24gdGhlIGZpcnN0IGFuZCBzZWNvbmRcbiAgIyBwb3NpdGlvbnMgY29udGFpbmluZyB0aGUgYWx0ZXJuYXRpdmUgYWxsZWxlLiBJZiBhbGwgcG9zaXRpb25zIGNvbnRhaW4gdGhlIFxuICAjIHJlZmVyZW5jZSBhbGxlbGUsIHRoZSB2YWx1ZSBpcyBzdWJ0cmFjdGVkIGZyb20gYWxsIHZhbHVlcyBpbiB0aGUgbWF0cml4XG4gICMgd2hlbiBqdXN0IG9uZSBwb3NpdGlvbiBpcyB0aGUgYWx0ZXJuYXRpdmUgYWxsZWxlLCB0aGUgdmFsdWUgaW4gdGhlIGRpYWdvbmFsXG4gICMgaXMgc2V0LiB3aGVuIHRoZXJlIGFyZSBtb3JlIHRoYW4gMiByZWdpb25zIGNvbnRhaW5pbmcgdGhlIGFsdGVybmF0aXZlIGFsbGVsZVxuICAjIHNraXAuXG4gICMgaWYgXFxuZi5hcy5uYSA9IFRSVUVcXCwgZXZhbHVhdGUgaWYgdGhlIGFjdGl2aXR5IG9mIGVpdGhlciBvZiB0aGUgdHdvIGNoaW1lcmFzXG4gICMgYmVpbmcgY29tcGFyZWQgaXMgbm9uIGZ1bmN0aW9uYWwuIGlmIHllcywgc2V0IHRoZSBjb3JyZXNwb25kaW5nIG1hdHJpeCB2YWx1ZVxuICAjIHRvIE5BXG4gIG91dF9tYXQgPC0gbWF0cml4KE5BLCBucm93ID0gNSwgbmNvbCA9IDUpXG4gIHJlZl92YWwgPC0gTkFcbiAgZGF0IDwtIGZpbHRlcih4aW1lcmEsIHNldCA9PSBcXE1cXCkgJT4lIFxuICAgIG11dGF0ZShTID0gYXMuY2hhcmFjdGVyKHN5bWJvbCkgJT4lIHRvdXBwZXIoKSlcbiAgaWYobmYuYXMubmEpe1xuICAgIGRhdCA8LSBmaWx0ZXIoZGF0LCBncm91cCAhPSBcXG4uZi5cXClcbiAgfVxuICBmb3IoaSBpbiBzZXEoMSwgbnJvdyhkYXQpKSl7XG4gICAgc3ltYm9sID0gZGF0W2ksIFxcU1xcXVxuICAgICMgZGV0ZXJtaW5lIHdoaWNoIHBvc2l0aW9ucyBjb250YWluIHRoZSBhbHRlcm5hdGl2ZSBhbGxlbGVcbiAgICBwID0gc3RyX2xvY2F0ZV9hbGwoc3ltYm9sLCBhbHQpW1sxXV1bLFxcc3RhcnRcXF1cbiAgICBsID0gbGVuZ3RoKHApICAgIyBob3cgbWFueSBwb3NpdGlvbnMgY29udGFpbiB0aGUgYWx0IGFsbGVsZVxuICAgIHYgPSBkYXRbW3Zhcl1dW2ldICMgcmV0cmlldmUgdGhlIHZhbHVlIG9mIHRoZSB2YXJpYWJsZVxuICAgIGlmKGwgPT0gMClcbiAgICAgIHJlZl92YWwgPSB2XG4gICAgZWxzZSBpZihsID09IDEpXG4gICAgICBvdXRfbWF0W3AsIHBdID0gdlxuICAgIGVsc2UgaWYobCA9PSAyKVxuICAgICAgb3V0X21hdFtwWzFdLCBwWzJdXSA9IHZcbiAgfVxuICBvdXRfbWF0ID0gb3V0X21hdCAtIHJlZl92YWxcbiAgcmV0dXJuKG91dF9tYXQpXG59XG5gYGBcbmBgYCJ9 -->

```r
```r
my_upper_triangular_mat <- function(alt = \C\, var = \A_PHO2\, nf.as.na = F){
  # given the alternative allele (C/S) and a variable of interest, e.g., A_PHO2,
  # output an upper triangular matrix containing the values from the variable 
  # of interest, with the row and col numbers based on the first and second
  # positions containing the alternative allele. If all positions contain the 
  # reference allele, the value is subtracted from all values in the matrix
  # when just one position is the alternative allele, the value in the diagonal
  # is set. when there are more than 2 regions containing the alternative allele
  # skip.
  # if \nf.as.na = TRUE\, evaluate if the activity of either of the two chimeras
  # being compared is non functional. if yes, set the corresponding matrix value
  # to NA
  out_mat <- matrix(NA, nrow = 5, ncol = 5)
  ref_val <- NA
  dat <- filter(ximera, set == \M\) %>% 
    mutate(S = as.character(symbol) %>% toupper())
  if(nf.as.na){
    dat <- filter(dat, group != \n.f.\)
  }
  for(i in seq(1, nrow(dat))){
    symbol = dat[i, \S\]
    # determine which positions contain the alternative allele
    p = str_locate_all(symbol, alt)[[1]][,\start\]
    l = length(p)   # how many positions contain the alt allele
    v = dat[[var]][i] # retrieve the value of the variable
    if(l == 0)
      ref_val = v
    else if(l == 1)
      out_mat[p, p] = v
    else if(l == 2)
      out_mat[p[1], p[2]] = v
  }
  out_mat = out_mat - ref_val
  return(out_mat)
}

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->



<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxubXlfY29tYmluZWRfdHJpYW5ndWxhcl9tYXQgPC0gZnVuY3Rpb24oYWx0ID0gXFxDXFwpe1xuICAjIGdpdmVuIHRoZSBhbHRlcm5hdGl2ZSBhbGxlbGUgKEMvUyksIG91dHB1dCBhIG1hdHJpeCBjb250YWluaW5nIHRoZSB2YWx1ZXNcbiAgIyBmb3IgYm90aCB3aXRoIGFuZCB3aXRob3V0IFBobzIsIGFycmFuZ2VkIGluIHR3byBjb21wbGVtZW50YXJ5IHRyaWFndWxhclxuICAjIG1hdHJpY2VzLCB3aXRoIHRoZSByb3cgYW5kIGNvbCBudW1iZXJzIGJhc2VkIG9uIHRoZSBmaXJzdCBhbmQgc2Vjb25kXG4gICMgcG9zaXRpb25zIGNvbnRhaW5pbmcgdGhlIGFsdGVybmF0aXZlIGFsbGVsZS4gSWYgYWxsIHBvc2l0aW9ucyBjb250YWluIHRoZSBcbiAgIyByZWZlcmVuY2UgYWxsZWxlLCB0aGUgdmFsdWUgaXMgc3VidHJhY3RlZCBmcm9tIGFsbCB2YWx1ZXMgaW4gdGhlIG1hdHJpeFxuICAjIHdoZW4ganVzdCBvbmUgcG9zaXRpb24gaXMgdGhlIGFsdGVybmF0aXZlIGFsbGVsZSwgdGhlIHZhbHVlIGluIHRoZSBkaWFnb25hbFxuICAjIGlzIHNldC4gd2hlbiB0aGVyZSBhcmUgbW9yZSB0aGFuIDIgcmVnaW9ucyBjb250YWluaW5nIHRoZSBhbHRlcm5hdGl2ZSBhbGxlbGVcbiAgIyBza2lwLlxuICBvdXRfbWF0IDwtIG1hdHJpeChOQSwgbnJvdyA9IDYsIG5jb2wgPSA2KVxuICB1cHBlciA8LSBjYmluZChOQSwgbXlfdXBwZXJfdHJpYW5ndWxhcl9tYXQoYWx0LCB2YXIgPSBcXEFfUEhPMlxcLCApKSAlPiUgXG4gICAgcmJpbmQoLiwgTkEpXG4gIGxvd2VyIDwtIHJiaW5kKE5BLCB0KG15X3VwcGVyX3RyaWFuZ3VsYXJfbWF0KGFsdCwgdmFyID0gXFxBX3BobzJcXCkpKSAlPiUgXG4gICAgY2JpbmQoLiwgTkEpXG4gIG91dF9tYXQgPSBpZmVsc2UoaXMubmEodXBwZXIpLCBsb3dlciwgdXBwZXIpXG4gIHJldHVybihvdXRfbWF0KVxufVxuYGBgXG5gYGAifQ== -->

```r
```r
my_combined_triangular_mat <- function(alt = \C\){
  # given the alternative allele (C/S), output a matrix containing the values
  # for both with and without Pho2, arranged in two complementary triagular
  # matrices, with the row and col numbers based on the first and second
  # positions containing the alternative allele. If all positions contain the 
  # reference allele, the value is subtracted from all values in the matrix
  # when just one position is the alternative allele, the value in the diagonal
  # is set. when there are more than 2 regions containing the alternative allele
  # skip.
  out_mat <- matrix(NA, nrow = 6, ncol = 6)
  upper <- cbind(NA, my_upper_triangular_mat(alt, var = \A_PHO2\, )) %>% 
    rbind(., NA)
  lower <- rbind(NA, t(my_upper_triangular_mat(alt, var = \A_pho2\))) %>% 
    cbind(., NA)
  out_mat = ifelse(is.na(upper), lower, upper)
  return(out_mat)
}

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->



<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxubXlfcGxvdF90cmlhbmdsZV9oZWF0bWFwIDwtIGZ1bmN0aW9uKGFsdCwgdmFyKXtcbiAgIyB0aGlzIGZ1bmN0aW9uIHRha2VzIHRoZSBvdXRwdXQgb2YgdGhlIGZ1bmN0aW9uIGFib3ZlIGFuZCBtYWtlcyBhIGhlYXRtYXBcbiAgIyB1c2luZyBwaGVhdG1hcCBmdW5jdGlvbiwgdGhlbiByb3RhdGVzIGl0IHVzaW5nIGdyaWQgZ3JhcGhpY3NcbiAgIyB0aGFua3MgdG8gaHR0cHM6Ly9ib29rZG93bi5vcmcvcmRwZW5nL1JQcm9nREEvdGhlLWdyaWQtcGFja2FnZS5odG1sI2dyaWQtZ3JhcGhpY3MtY29vcmRpbmF0ZS1zeXN0ZW1zXG4gICMgYWRkaW5nIHRpdGxlIGJhc2VkIG9uIGh0dHBzOi8vZGF2ZXRhbmcuZ2l0aHViLmlvL211c2UvcGhlYXRtYXAuaHRtbFxuICBcbiAgIyBjb25zdHJ1Y3QgdGl0bGUgb2YgcGxvdFxuICByZWYgPSBpZmVsc2UoYWx0ID09IFxcQ1xcLCBcXFNjUGhvNFxcLCBcXENnUGhvNFxcKVxuICBiZyA9IGlmZWxzZSh2YXIgPT0gXFxBX1BITzJcXCwgXFx3aXRoIFBITzJcXCwgXFx3L28gcGhvMlxcKVxuICBteV90aXRsZSA8LSBwYXN0ZShcXEVwaXN0YXNpcyBiZXR3ZWVuIHJlZ2lvbnMgb25cXCwgcmVmLCBcXGJhY2tncm91bmRcXCwgYmcpXG4gIHRlc3QgPC0gbXlfdXBwZXJfdHJpYW5ndWxhcl9tYXQoYWx0ID0gYWx0LCB2YXIgPSB2YXIpXG4gIHBhbGV0dGVMZW5ndGggPSA1MFxuICBteUNvbG9ycyA8LSBjb2xvclJhbXBQYWxldHRlKGMoXFxzdGVlbGJsdWUzXFwsIFxcZ3JheTkwXFwsIFxccmVkXFwpKShwYWxldHRlTGVuZ3RoKVxuICBybmcgPC0gbWF4KGFicyh0ZXN0KSwgbmEucm0gPSBUUlVFKVxuICBteUJyZWFrcyA8LSBjKHNlcSgtcm5nLCAwLCBsZW5ndGgub3V0PWNlaWxpbmcocGFsZXR0ZUxlbmd0aC8yKSArIDEpLCBcbiAgICAgICAgICAgICAgICBzZXEocm5nL3BhbGV0dGVMZW5ndGgsIHJuZyxcbiAgICAgICAgICAgICAgICAgICAgbGVuZ3RoLm91dD1mbG9vcihwYWxldHRlTGVuZ3RoLzIpKSlcbiAgcCA8LSBwaGVhdG1hcDo6cGhlYXRtYXAodGVzdCwgY29sb3IgPSBteUNvbG9ycywgYnJlYWtzID0gbXlCcmVha3MsXG4gICAgICAgICAgICAgICAgICAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLCBuYV9jb2wgPSBOQSwgc2lsZW50ID0gVFJVRSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2xzID0gRkFMU0UsIGNsdXN0ZXJfcm93cyA9IEZBTFNFKVxuICB2cCA8LSB2aWV3cG9ydCh4ID0gMC41LCB5ID0gMC4yNSxcbiAgICAgICAgICAgICAgICAgd2lkdGggPSB1bml0KDQuNSwgXFxpblxcKSwgaGVpZ2h0ID0gdW5pdCg0LjUsIFxcaW5cXCksIGFuZ2xlID0gNDcpIFxuICBncmlkLm5ld3BhZ2UoKVxuICBwdXNoVmlld3BvcnQodnApXG4gIGdyaWQuZHJhdyhwJGd0YWJsZSlcbiAgcG9wVmlld3BvcnQoKVxuICBncmlkLnRleHQobGFiZWwgPSBteV90aXRsZSwgeCA9IDAuNSwgeSA9IDAuOTUsIGdwID0gZ3Bhcihmb250c2l6ZSA9IDE2LCBmb250ZmFjZSA9IFxcYm9sZFxcKSlcbiAgcmV0dXJuKHApXG59XG5gYGBcbmBgYCJ9 -->

```r
```r
my_plot_triangle_heatmap <- function(alt, var){
  # this function takes the output of the function above and makes a heatmap
  # using pheatmap function, then rotates it using grid graphics
  # thanks to https://bookdown.org/rdpeng/RProgDA/the-grid-package.html#grid-graphics-coordinate-systems
  # adding title based on https://davetang.github.io/muse/pheatmap.html
  
  # construct title of plot
  ref = ifelse(alt == \C\, \ScPho4\, \CgPho4\)
  bg = ifelse(var == \A_PHO2\, \with PHO2\, \w/o pho2\)
  my_title <- paste(\Epistasis between regions on\, ref, \background\, bg)
  test <- my_upper_triangular_mat(alt = alt, var = var)
  paletteLength = 50
  myColors <- colorRampPalette(c(\steelblue3\, \gray90\, \red\))(paletteLength)
  rng <- max(abs(test), na.rm = TRUE)
  myBreaks <- c(seq(-rng, 0, length.out=ceiling(paletteLength/2) + 1), 
                seq(rng/paletteLength, rng,
                    length.out=floor(paletteLength/2)))
  p <- pheatmap::pheatmap(test, color = myColors, breaks = myBreaks,
                          border_color = NA, na_col = NA, silent = TRUE,
                          cluster_cols = FALSE, cluster_rows = FALSE)
  vp <- viewport(x = 0.5, y = 0.25,
                 width = unit(4.5, \in\), height = unit(4.5, \in\), angle = 47) 
  grid.newpage()
  pushViewport(vp)
  grid.draw(p$gtable)
  popViewport()
  grid.text(label = my_title, x = 0.5, y = 0.95, gp = gpar(fontsize = 16, fontface = \bold\))
  return(p)
}

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


## P2ID:Cg_DBD:Sc fail

Highlight the subset of the chimeras with P2ID:Cg + DBD:Sc, most of which are non functional


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxubXlfcGxvdF9jb21iaW5lZF90cmlhbmdsZV9oZWF0bWFwIDwtIGZ1bmN0aW9uKGFsdCl7XG4gICMgdGhpcyBmdW5jdGlvbiB0YWtlcyB0aGUgb3V0cHV0IG9mIHRoZSBmdW5jdGlvbiBteV9jb21iaW5lZF90cmlhbmd1bGFyX21hdCgpXG4gICMgdXNpbmcgcGhlYXRtYXAgZnVuY3Rpb24sIHRoZW4gcm90YXRlcyBpdCB1c2luZyBncmlkIGdyYXBoaWNzXG4gICMgdGhhbmtzIHRvIGh0dHBzOi8vYm9va2Rvd24ub3JnL3JkcGVuZy9SUHJvZ0RBL3RoZS1ncmlkLXBhY2thZ2UuaHRtbCNncmlkLWdyYXBoaWNzLWNvb3JkaW5hdGUtc3lzdGVtc1xuICAjIGFkZGluZyB0aXRsZSBiYXNlZCBvbiBodHRwczovL2RhdmV0YW5nLmdpdGh1Yi5pby9tdXNlL3BoZWF0bWFwLmh0bWxcbiAgXG4gICMgY29uc3RydWN0IHRpdGxlIG9mIHBsb3RcbiAgcmVmID0gaWZlbHNlKGFsdCA9PSBcXENcXCwgXFxTY1BobzRcXCwgXFxDZ1BobzRcXClcbiAgbXlfdGl0bGUgPC0gcGFzdGUoXFxFcGlzdGFzaXMgYmV0d2VlbiByZWdpb25zIG9uXFwsIHJlZiwgXFxiYWNrZ3JvdW5kXFwpXG4gIHRlc3QgPC0gbXlfY29tYmluZWRfdHJpYW5ndWxhcl9tYXQoYWx0ID0gYWx0KVxuICBwYWxldHRlTGVuZ3RoID0gNTBcbiAgbXlDb2xvcnMgPC0gY29sb3JSYW1wUGFsZXR0ZShjKFxcc3RlZWxibHVlXFwsIFxcZ3JheTkwXFwsIFxccmVkXFwpKShwYWxldHRlTGVuZ3RoKVxuICBybmcgPC0gbWF4KGFicyh0ZXN0KSwgbmEucm0gPSBUUlVFKVxuICBteUJyZWFrcyA8LSBjKHNlcSgtcm5nLCAwLCBsZW5ndGgub3V0PWNlaWxpbmcocGFsZXR0ZUxlbmd0aC8yKSArIDEpLCBcbiAgICAgICAgICAgICAgICBzZXEocm5nL3BhbGV0dGVMZW5ndGgsIHJuZyxcbiAgICAgICAgICAgICAgICAgICAgbGVuZ3RoLm91dD1mbG9vcihwYWxldHRlTGVuZ3RoLzIpKSlcbiAgcCA8LSBwaGVhdG1hcDo6cGhlYXRtYXAodGVzdCwgY29sb3IgPSBteUNvbG9ycywgYnJlYWtzID0gbXlCcmVha3MsXG4gICAgICAgICAgICAgICAgICAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLCBuYV9jb2wgPSBOQSwgc2lsZW50ID0gVFJVRSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2xzID0gRkFMU0UsIGNsdXN0ZXJfcm93cyA9IEZBTFNFKVxuICB2cCA8LSB2aWV3cG9ydCh4ID0gMC41LCB5ID0gMC40NSxcbiAgICAgICAgICAgICAgICAgd2lkdGggPSB1bml0KDMsIFxcaW5cXCksIGhlaWdodCA9IHVuaXQoMi44LCBcXGluXFwpLCBhbmdsZSA9IDQ3KSBcbiAgZ3JpZC5uZXdwYWdlKClcbiAgcHVzaFZpZXdwb3J0KHZwKVxuICBncmlkLmRyYXcocCRndGFibGUpXG4gIHBvcFZpZXdwb3J0KClcbiAgZ3JpZC50ZXh0KGxhYmVsID0gbXlfdGl0bGUsIHggPSAwLjUsIHkgPSAwLjk1LCBcbiAgICAgICAgICAgIGdwID0gZ3Bhcihmb250c2l6ZSA9IDE2LCBmb250ZmFjZSA9IFxcYm9sZFxcKSlcbiAgZ3JpZC50ZXh0KGxhYmVsID0gXFxXaXRoIFBobzJcXCwgeCA9IDAuMSwgeSA9IDAuNjUsIGp1c3QgPSBjKFxcbGVmdFxcLCBcXHRvcFxcKSxcbiAgICAgICAgICAgIGdwID0gZ3Bhcihmb250c2l6ZSA9IDE0LCBmb250ZmFjZSA9IFxcYm9sZFxcKSlcbiAgZ3JpZC50ZXh0KGxhYmVsID0gXFxXaXRob3V0IHBobzJcXCwgeCA9IDAuMSwgeSA9IDAuMjUsIGp1c3QgPSBjKFxcbGVmdFxcLCBcXHRvcFxcKSwgXG4gICAgICAgICAgICBncCA9IGdwYXIoZm9udHNpemUgPSAxNCwgZm9udGZhY2UgPSBcXGJvbGRcXCkpXG4gIHJldHVybihwKVxufVxuYGBgXG5gYGAifQ== -->

```r
```r
my_plot_combined_triangle_heatmap <- function(alt){
  # this function takes the output of the function my_combined_triangular_mat()
  # using pheatmap function, then rotates it using grid graphics
  # thanks to https://bookdown.org/rdpeng/RProgDA/the-grid-package.html#grid-graphics-coordinate-systems
  # adding title based on https://davetang.github.io/muse/pheatmap.html
  
  # construct title of plot
  ref = ifelse(alt == \C\, \ScPho4\, \CgPho4\)
  my_title <- paste(\Epistasis between regions on\, ref, \background\)
  test <- my_combined_triangular_mat(alt = alt)
  paletteLength = 50
  myColors <- colorRampPalette(c(\steelblue\, \gray90\, \red\))(paletteLength)
  rng <- max(abs(test), na.rm = TRUE)
  myBreaks <- c(seq(-rng, 0, length.out=ceiling(paletteLength/2) + 1), 
                seq(rng/paletteLength, rng,
                    length.out=floor(paletteLength/2)))
  p <- pheatmap::pheatmap(test, color = myColors, breaks = myBreaks,
                          border_color = NA, na_col = NA, silent = TRUE,
                          cluster_cols = FALSE, cluster_rows = FALSE)
  vp <- viewport(x = 0.5, y = 0.45,
                 width = unit(3, \in\), height = unit(2.8, \in\), angle = 47) 
  grid.newpage()
  pushViewport(vp)
  grid.draw(p$gtable)
  popViewport()
  grid.text(label = my_title, x = 0.5, y = 0.95, 
            gp = gpar(fontsize = 16, fontface = \bold\))
  grid.text(label = \With Pho2\, x = 0.1, y = 0.65, just = c(\left\, \top\),
            gp = gpar(fontsize = 14, fontface = \bold\))
  grid.text(label = \Without pho2\, x = 0.1, y = 0.25, just = c(\left\, \top\), 
            gp = gpar(fontsize = 14, fontface = \bold\))
  return(p)
}

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->



<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxucG5nKFxcLi4vaW1nLzIwMjQwMTE1LXRyaWFuZ2xlLWhlYXRtYXAtQ2dQaG80LXJlZi5wbmdcXCwgd2lkdGggPSA3LCBoZWlnaHQgPSA1LCB1bml0cyA9IFxcaW5cXCwgcmVzID0gMzAwKVxucDEgPC0gbXlfcGxvdF9jb21iaW5lZF90cmlhbmdsZV9oZWF0bWFwKFxcQ1xcKVxuZGV2Lm9mZigpXG5wbmcoXFwuLi9pbWcvMjAyNDAxMTUtdHJpYW5nbGUtaGVhdG1hcC1TY1BobzQtcmVmLnBuZ1xcLCB3aWR0aCA9IDcsIGhlaWdodCA9IDUsIHVuaXRzID0gXFxpblxcLCByZXMgPSAzMDApXG5wMiA8LSBteV9wbG90X2NvbWJpbmVkX3RyaWFuZ2xlX2hlYXRtYXAoXFxTXFwpXG5kZXYub2ZmKClcbmBgYFxuYGBgIn0= -->

```r
```r
png(\../img/20240115-triangle-heatmap-CgPho4-ref.png\, width = 7, height = 5, units = \in\, res = 300)
p1 <- my_plot_combined_triangle_heatmap(\C\)
dev.off()
png(\../img/20240115-triangle-heatmap-ScPho4-ref.png\, width = 7, height = 5, units = \in\, res = 300)
p2 <- my_plot_combined_triangle_heatmap(\S\)
dev.off()

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


## Triangle heatmap

First, write a function to generate the data for plotting. If we are going to use ggplot, we need a tibble to store the data, something in the following form

| plasmid | symbol | RegionA | RegionB | A_PHO2 | A_pho2 | rA_PHO2 | boost | perc_pho2 |
|:--------|:-------|:--------|:--------|:-------|:-------|:--------|:------|:----------|
| 209     | CCSCC  | 3       | 3       | 8.25   | 7.82   | 0.468   | 1.06  | 0.94      |

If we are ok with using non ggplot - heatmaps are not ggplot's strength anyways - we can just build a matrix.

Note that this way of summarizing the data has many limitaitons: 1) it requires specifying the reference, either CCCCC or SSSSS. Everything is measured against that; 2) it only shows pairwise (two region) interactions. This turns out to be fine with five regions, since every chimera can be expressed as either a 0, 1 or 2 region swap from one of the two reference genotypes. With 6 or more regions, higher level (3 or more region) interactions cannot be visualized this way. Because of this, we will focus on just the main set for this analysis.

To build the matrix, we need to first identify the chimeras that belong to the set. For that, we will use the "main" set, with the five region split, for the moment at least. The function will first determine which reference to use. If we use SSSSS as the reference, for example, we will assign 0 to the reference. All other chimeras with 1 or 2 regions from Cg will be used to fill an upper triangular matrix, using one of the values of interest, e.g., A_PHO2.


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxubXlfdXBwZXJfdHJpYW5ndWxhcl9tYXQgPC0gZnVuY3Rpb24oYWx0ID0gXCJDXCIsIHZhciA9IFwiQV9QSE8yXCIsIG5mLmFzLm5hID0gRil7XG4gICMgZ2l2ZW4gdGhlIGFsdGVybmF0aXZlIGFsbGVsZSAoQy9TKSBhbmQgYSB2YXJpYWJsZSBvZiBpbnRlcmVzdCwgZS5nLiwgQV9QSE8yLFxuICAjIG91dHB1dCBhbiB1cHBlciB0cmlhbmd1bGFyIG1hdHJpeCBjb250YWluaW5nIHRoZSB2YWx1ZXMgZnJvbSB0aGUgdmFyaWFibGUgXG4gICMgb2YgaW50ZXJlc3QsIHdpdGggdGhlIHJvdyBhbmQgY29sIG51bWJlcnMgYmFzZWQgb24gdGhlIGZpcnN0IGFuZCBzZWNvbmRcbiAgIyBwb3NpdGlvbnMgY29udGFpbmluZyB0aGUgYWx0ZXJuYXRpdmUgYWxsZWxlLiBJZiBhbGwgcG9zaXRpb25zIGNvbnRhaW4gdGhlIFxuICAjIHJlZmVyZW5jZSBhbGxlbGUsIHRoZSB2YWx1ZSBpcyBzdWJ0cmFjdGVkIGZyb20gYWxsIHZhbHVlcyBpbiB0aGUgbWF0cml4XG4gICMgd2hlbiBqdXN0IG9uZSBwb3NpdGlvbiBpcyB0aGUgYWx0ZXJuYXRpdmUgYWxsZWxlLCB0aGUgdmFsdWUgaW4gdGhlIGRpYWdvbmFsXG4gICMgaXMgc2V0LiB3aGVuIHRoZXJlIGFyZSBtb3JlIHRoYW4gMiByZWdpb25zIGNvbnRhaW5pbmcgdGhlIGFsdGVybmF0aXZlIGFsbGVsZVxuICAjIHNraXAuXG4gICMgaWYgXCJuZi5hcy5uYSA9IFRSVUVcIiwgZXZhbHVhdGUgaWYgdGhlIGFjdGl2aXR5IG9mIGVpdGhlciBvZiB0aGUgdHdvIGNoaW1lcmFzXG4gICMgYmVpbmcgY29tcGFyZWQgaXMgbm9uIGZ1bmN0aW9uYWwuIGlmIHllcywgc2V0IHRoZSBjb3JyZXNwb25kaW5nIG1hdHJpeCB2YWx1ZVxuICAjIHRvIE5BXG4gIG91dF9tYXQgPC0gbWF0cml4KE5BLCBucm93ID0gNSwgbmNvbCA9IDUpXG4gIHJlZl92YWwgPC0gTkFcbiAgZGF0IDwtIGZpbHRlcih4aW1lcmEsIHNldCA9PSBcIk1cIikgJT4lIFxuICAgIG11dGF0ZShTID0gYXMuY2hhcmFjdGVyKHN5bWJvbCkgJT4lIHRvdXBwZXIoKSlcbiAgaWYobmYuYXMubmEpe1xuICAgIGRhdCA8LSBmaWx0ZXIoZGF0LCBncm91cCAhPSBcIm4uZi5cIilcbiAgfVxuICBmb3IoaSBpbiBzZXEoMSwgbnJvdyhkYXQpKSl7XG4gICAgc3ltYm9sID0gZGF0W2ksIFwiU1wiXVxuICAgICMgZGV0ZXJtaW5lIHdoaWNoIHBvc2l0aW9ucyBjb250YWluIHRoZSBhbHRlcm5hdGl2ZSBhbGxlbGVcbiAgICBwID0gc3RyX2xvY2F0ZV9hbGwoc3ltYm9sLCBhbHQpW1sxXV1bLFwic3RhcnRcIl1cbiAgICBsID0gbGVuZ3RoKHApICAgIyBob3cgbWFueSBwb3NpdGlvbnMgY29udGFpbiB0aGUgYWx0IGFsbGVsZVxuICAgIHYgPSBkYXRbW3Zhcl1dW2ldICMgcmV0cmlldmUgdGhlIHZhbHVlIG9mIHRoZSB2YXJpYWJsZVxuICAgIGlmKGwgPT0gMClcbiAgICAgIHJlZl92YWwgPSB2XG4gICAgZWxzZSBpZihsID09IDEpXG4gICAgICBvdXRfbWF0W3AsIHBdID0gdlxuICAgIGVsc2UgaWYobCA9PSAyKVxuICAgICAgb3V0X21hdFtwWzFdLCBwWzJdXSA9IHZcbiAgfVxuICBvdXRfbWF0ID0gb3V0X21hdCAtIHJlZl92YWxcbiAgcmV0dXJuKG91dF9tYXQpXG59XG5gYGAifQ== -->

```r
my_upper_triangular_mat <- function(alt = "C", var = "A_PHO2", nf.as.na = F){
  # given the alternative allele (C/S) and a variable of interest, e.g., A_PHO2,
  # output an upper triangular matrix containing the values from the variable 
  # of interest, with the row and col numbers based on the first and second
  # positions containing the alternative allele. If all positions contain the 
  # reference allele, the value is subtracted from all values in the matrix
  # when just one position is the alternative allele, the value in the diagonal
  # is set. when there are more than 2 regions containing the alternative allele
  # skip.
  # if "nf.as.na = TRUE", evaluate if the activity of either of the two chimeras
  # being compared is non functional. if yes, set the corresponding matrix value
  # to NA
  out_mat <- matrix(NA, nrow = 5, ncol = 5)
  ref_val <- NA
  dat <- filter(ximera, set == "M") %>% 
    mutate(S = as.character(symbol) %>% toupper())
  if(nf.as.na){
    dat <- filter(dat, group != "n.f.")
  }
  for(i in seq(1, nrow(dat))){
    symbol = dat[i, "S"]
    # determine which positions contain the alternative allele
    p = str_locate_all(symbol, alt)[[1]][,"start"]
    l = length(p)   # how many positions contain the alt allele
    v = dat[[var]][i] # retrieve the value of the variable
    if(l == 0)
      ref_val = v
    else if(l == 1)
      out_mat[p, p] = v
    else if(l == 2)
      out_mat[p[1], p[2]] = v
  }
  out_mat = out_mat - ref_val
  return(out_mat)
}
my_combined_triangular_mat <- function(alt = "C"){
  # given the alternative allele (C/S), output a matrix containing the values
  # for both with and without Pho2, arranged in two complementary triagular
  # matrices, with the row and col numbers based on the first and second
  # positions containing the alternative allele. If all positions contain the 
  # reference allele, the value is subtracted from all values in the matrix
  # when just one position is the alternative allele, the value in the diagonal
  # is set. when there are more than 2 regions containing the alternative allele
  # skip.
  out_mat <- matrix(NA, nrow = 6, ncol = 6)
  upper <- cbind(NA, my_upper_triangular_mat(alt, var = "A_PHO2", )) %>% 
    rbind(., NA)
  lower <- rbind(NA, t(my_upper_triangular_mat(alt, var = "A_pho2"))) %>% 
    cbind(., NA)
  out_mat = ifelse(is.na(upper), lower, upper)
  return(out_mat)
}
my_plot_triangle_heatmap <- function(alt, var){
  # this function takes the output of the function above and makes a heatmap
  # using pheatmap function, then rotates it using grid graphics
  # thanks to https://bookdown.org/rdpeng/RProgDA/the-grid-package.html#grid-graphics-coordinate-systems
  # adding title based on https://davetang.github.io/muse/pheatmap.html
  
  # construct title of plot
  ref = ifelse(alt == "C", "ScPho4", "CgPho4")
  bg = ifelse(var == "A_PHO2", "with PHO2", "w/o pho2")
  my_title <- paste("Epistasis between regions on", ref, "background", bg)
  test <- my_upper_triangular_mat(alt = alt, var = var)
  paletteLength = 50
  myColors <- colorRampPalette(c("steelblue3", "gray90", "red"))(paletteLength)
  rng <- max(abs(test), na.rm = TRUE)
  myBreaks <- c(seq(-rng, 0, length.out=ceiling(paletteLength/2) + 1), 
                seq(rng/paletteLength, rng,
                    length.out=floor(paletteLength/2)))
  p <- pheatmap::pheatmap(test, color = myColors, breaks = myBreaks,
                          border_color = NA, na_col = NA, silent = TRUE,
                          cluster_cols = FALSE, cluster_rows = FALSE)
  vp <- viewport(x = 0.5, y = 0.25,
                 width = unit(4.5, "in"), height = unit(4.5, "in"), angle = 47) 
  grid.newpage()
  pushViewport(vp)
  grid.draw(p$gtable)
  popViewport()
  grid.text(label = my_title, x = 0.5, y = 0.95, gp = gpar(fontsize = 16, fontface = "bold"))
  return(p)
}
my_plot_combined_triangle_heatmap <- function(alt){
  # this function takes the output of the function my_combined_triangular_mat()
  # using pheatmap function, then rotates it using grid graphics
  # thanks to https://bookdown.org/rdpeng/RProgDA/the-grid-package.html#grid-graphics-coordinate-systems
  # adding title based on https://davetang.github.io/muse/pheatmap.html
  
  # construct title of plot
  ref = ifelse(alt == "C", "ScPho4", "CgPho4")
  my_title <- paste("Epistasis between regions on", ref, "background")
  test <- my_combined_triangular_mat(alt = alt)
  paletteLength = 50
  myColors <- colorRampPalette(c("steelblue", "gray90", "red"))(paletteLength)
  rng <- max(abs(test), na.rm = TRUE)
  myBreaks <- c(seq(-rng, 0, length.out=ceiling(paletteLength/2) + 1), 
                seq(rng/paletteLength, rng,
                    length.out=floor(paletteLength/2)))
  p <- pheatmap::pheatmap(test, color = myColors, breaks = myBreaks,
                          border_color = NA, na_col = NA, silent = TRUE,
                          cluster_cols = FALSE, cluster_rows = FALSE)
  vp <- viewport(x = 0.5, y = 0.45,
                 width = unit(3, "in"), height = unit(2.8, "in"), angle = 47) 
  grid.newpage()
  pushViewport(vp)
  grid.draw(p$gtable)
  popViewport()
  grid.text(label = my_title, x = 0.5, y = 0.95, 
            gp = gpar(fontsize = 16, fontface = "bold"))
  grid.text(label = "With Pho2", x = 0.1, y = 0.65, just = c("left", "top"),
            gp = gpar(fontsize = 14, fontface = "bold"))
  grid.text(label = "Without pho2", x = 0.1, y = 0.25, just = c("left", "top"), 
            gp = gpar(fontsize = 14, fontface = "bold"))
  return(p)
}
png("../img/20240115-triangle-heatmap-CgPho4-ref.png", width = 7, height = 5, units = "in", res = 300)
p1 <- my_plot_combined_triangle_heatmap("C")
dev.off()
png("../img/20240115-triangle-heatmap-ScPho4-ref.png", width = 7, height = 5, units = "in", res = 300)
p2 <- my_plot_combined_triangle_heatmap("S")
dev.off()
LS0tCnRpdGxlOiAiRTAxMyBQaG80IGNoaW1lcmEgYWN0aXZpdHkgYW5hbHlzaXMgdXNpbmcgUEhPNSByZXBvcnRlciwgYW5hbHlzaXMiCmF1dGhvcjogIkJpbiBIZSIKZGF0ZTogIjIwMjMtMTAtMzEgdXBkYXRlZCBgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICBkZl9wcmludDogcGFnZWQKLS0tCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpyZXF1aXJlKHBsb3RseSkKcmVxdWlyZSh0aWR5dmVyc2UpCnJlcXVpcmUoZ2dyaWRnZXMpCnJlcXVpcmUoY293cGxvdCkKcmVxdWlyZShSQ29sb3JCcmV3ZXIpCnJlcXVpcmUoZ3JpZCkKcmVxdWlyZShnZ3RleHQpCmBgYAoKYGBge3J9Cm9sZCA8LSB0aGVtZV9zZXQodGhlbWVfYncoYmFzZV9zaXplID0gMTYpKQpgYGAKClRoaXMgaXMgdGhlIHNlY29uZCBwYXJ0IG9mIHRoZSBhbmFseXNpcy4gSW4gdGhlIGZpcnN0IHBhcnQgKHNlZSBgLi4vaW5wdXQvUEhPNS1kYXRhLzIwMjMxMDE5LXBvb2wtcWMtUEhPNS5SbWRgKSwgSSBkaWQgUUMgYW5kIGV4cG9ydGVkIHRoZSBmaWx0ZXJlZCBkYXRhc2V0LiBIZXJlLCBJIHdpbGwgY29udGludWUgd29ya2luZyB3aXRoIHRoYXQgZGF0YXNldCBhbmQgYW5zd2VyIG91ciBiaW9sb2dpY2FsIHF1ZXN0aW9ucy4KCiMgR29hbAoKLSAgIEFuYWx5emUgdGhlIGZ1bGwgY2hpbWVyYSBzZXQgZmxvdyByZXN1bHRzIGZvciAqUEhPNXByKi1tQ2hlcnJ5IHJlcG9ydGVyLgotICAgRGV2ZWxvcCBhbiBhbmFseXNpcyBwaXBlbGluZSB0byBwZXJmb3JtIFFDLCBjb3JyZWN0aW9uIChpZiBuZWVkZWQpIGFuZCBwbG90dGluZyB0aGUgcmVzdWx0cy4KCiMgRGF0YQoKSW1wb3J0IHRoZSBiYWNrZ3JvdW5kIHN1YnRyYWN0ZWQgZGF0YQoKYGBge3J9CmRhdDAgPC0gcmVhZF90c3YoIi4uL2lucHV0LzIwMjMxMDIzLVBITzUtYmctc3VidHJhY3RlZC1kYXRhLnRzdiIsIGNvbF90eXBlcyA9ICJjY2NjZGRkZGRjIikKYGBgCgpGaWx0ZXIgdGhlIGRhdGEKCmBgYHtyfQpkYXQgPC0gZmlsdGVyKGRhdDAsIGhvc3QgIT0gIlBITzg0IiwgZmxhZyA9PSAicGFzcyIsIGRhdGUgIT0gIjAyLzEwIikgJT4lIAogICMgYmFzZWQgb24gcHJldmlvdXMgUUMsIHRoZSBmb2xsb3dpbmcgc2FtcGxlIChib3RoIHJlcGxpY2F0ZXMpIGhhdmUgaGlnaAogICMgdmFyaWFuY2UgLSBvbmUgYmlvbG9naWNhbCByZXBsaWNhdGUgaXMgaGlnaGx5IGV4cHJlc3NlZCwgd2hpbGUgdGhlIG90aGVyIAogICMgdHdvIGhhdmUgbU5lb24sIGJ1dCBiYXJlbHkgYW55IFJGUCBleHByZXNzaW9uLgogIG11dGF0ZSgKICAgIGhvc3QgPSBmY3RfcmVjb2RlKGhvc3QsIHBobzIgPSAicGhvMuKIhiIpLAogICAgZmxhZyA9IGlmZWxzZShwbGFzbWlkID09ICIyMzMiICYgaG9zdCA9PSAicGhvMiIsICJoaWdoLnZhciIsIGZsYWcpKQpgYGAKCk51bWJlciBvZiByZXBsaWNhdGVzIGxlZnQgZm9yIGVhY2ggc2FtcGxlCgpgYGB7cn0KZXhwdCA8LSBkYXQgJT4lIAogIGZpbHRlcihob3N0ICVpbiUgYygiUEhPMiIsICJwaG8yIiksICFwbGFzbWlkICVpbiUgYygiMTg4IiwgIjE5NCIpKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSwgcGxhc21pZCwgaG9zdCkgJT4lIAogIHN1bW1hcml6ZShuID0gbigpLCAuZ3JvdXBzID0gImRyb3AiKQoKZXhwdCAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gcGxhc21pZCwgeSA9IG4pKSArCiAgZ2VvbV9jb2woYWVzKGZpbGwgPSBob3N0KSkgKyAKICBmYWNldF9ncmlkKGRhdGUgfiAuKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiUEhPMiIgPSAiZ3JheTMwIiwgInBobzIiID0gImdyYXk3MCIpKSArCiAgdGhlbWVfbWluaW1hbCgpICsgYmFja2dyb3VuZF9ncmlkKG1ham9yID0gIm5vbmUiKSArIHBhbmVsX2JvcmRlcihzaXplID0gMC41KSArCiAgc2NhbGVfeV9jb250aW51b3VzKG5hbWUgPSAiUmVwbGljYXRlcyIsIGJyZWFrcyA9IGMoNikpICsgeGxhYihOVUxMKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCksCiAgICAgICAgc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKQ2hpbWVyYSBtYWtldXAgaW5mb3JtYXRpb24KCmBgYHtyfQptZXRhIDwtIHJlYWRfdHN2KCIuLi9pbnB1dC8yMDIzMDIwOC1jaGltZXJhLVBobzQtbWFrZXVwLnR4dCIsIGNvbF90eXBlcyA9ICJjY2NjYyIpCmBgYAoKIyMgU3VtbWFyaXplIGRhdGEKCkhlcmUgd2Ugd291bGQgbGlrZSBjYWxjdWxhdGUgdGhlIHJhdGlvIG9mIFJGUC9HRlAgZm9yIGVhY2ggY2hpbWVyYSAocGxhc21pZCkgYWNyb3NzIGFsbCByZXBsaWNhdGVzLCBpbmNsdWRpbmcgZnJvbSBkaWZmZXJlbnQgZGF5cy4gTm90ZSB0aGF0IHRoZSBwYXJhbWV0ZXIgb2YgaW50ZXJlc3QgaXMgYSByYXRpbywgd2hpY2ggY2FuIGJlIGVzdGltYXRlZCB1c2luZyBlaXRoZXIgIm1lYW5zIG9mIHJhdGlvcyIgb3IgInJhdGlvcyBvZiBtZWFucyIuIFRoZXNlIGFyZSBqdXN0IHR3byBzcGVjaWZpYyBpbnN0YW5jZXMgb2YgYSBtb3JlIGdlbmVyYWwgZXN0aW1hdG9yLCByZXByZXNlbnRpbmcgdHdvIGNob2ljZXMgb2YgdGhlIHdlaWdodHMuIFRoZSAibWVhbnMgb2YgcmF0aW9zIiBmaXJzdCBjYWxjdWxhdGVzIHRoZSByYXRpb3MgZm9yIGVhY2ggcmVwbGljYXRlIHdpdGhpbiBhIHBsYXNtaWQsIHRoZW4gYXZlcmFnZSB0aGVtLiBJbiB0aGlzIGNhbGN1bGF0aW9uLCBlYWNoIHJlcGxpY2F0ZSBpcyBnaXZlbiB0aGUgd2VpZ2h0IG9mIDEvbiAoZXF1YWwpLiBUaGUgInJhdGlvcyBvZiBtZWFucyIgZmlyc3Qgc3VtIHVwIHRoZSBHRlAgYW5kIFJGUCB2YWx1ZXMgc2VwYXJhdGVseSBhY3Jvc3MgdGhlIHJlcGxpY2F0ZXMgZm9yIGVhY2ggcGxhc21pZCwgdGhlbiB0YWtlIHRoZSByYXRpbyBiZXR3ZWVuIHRoZW0uIEluIHRoaXMgZXN0aW1hdG9yLCB0aGUgd2VpZ2h0IGZvciBlYWNoIHJlcGxpY2F0ZSBpcyB4IC8gc3VtKHgpLCB3aGVyZSB4IGlzIHRoZSBkZW5vbWluYXRvciBpbiB0aGUgcmF0aW8sIGkuZS4sIEdGUC4gSW4gb3RoZXIgd29yZHMsIHRoaXMgZXN0aW1hdG9yIHdpbGwgZ2l2ZSBtb3JlIHdlaWdodHMgdG8gdGhlIHJlcGxpY2F0ZXMgd2hlcmUgdGhlIGNoaW1lcmEgaGFkIGEgaGlnaGVyIGV4cHJlc3Npb24gbGV2ZWwuCgpCb3RoIGVzdGltYXRvcnMgYXJlIGtub3duIHRvIGJlIGJpYXNlZC4gV2Ugd2lsbCBpZ25vcmUgdGhhdCBmb3IgdGhlIG1vbWVudC4gSW4gdGVybXMgb2YgYSBjaG9pY2UgYmV0d2VlbiB0aGUgdHdvLCBpdCBzZWVtcyB0aGF0IHRoZXJlIGlzIG5vIHJlYXNvbiB0byBnaXZlIG1vcmUgd2VpZ2h0cyB0byB0aGUgZXhwZXJpbWVudHMgd2l0aCBhIGhpZ2hlciBHRlAgc2lnbmFsLiBTbywgdGhlICJtZWFucyBvZiByYXRpb3MiIHNlZW1zIGEgbW9yZSBuYXR1cmFsIGNob2ljZS4gSG93ZXZlciwgd2Ugd2lsbCBjYWxjdWx0YWUgYm90aCBhbmQgZGVjaWRlIGxhdGVyLgoKQSBmaW5hbCBxdWVzdGlvbiBpcyBob3cgdG8gY2FsY3VsYXRlIHRoZSB2YXJpYW5jZSBvZiB0aGUgcmF0aW8gZXN0aW1hdGUuIEFjY29yZGluZyB0byB0aGUgYHN1cnZleWAgcGFja2FnZSBbbWFudWFsXShodHRwczovL3JzdHVkaW8tcHVicy1zdGF0aWMuczMuYW1hem9uYXdzLmNvbS8xNzg5NjVfZmI2MGEwZjdiYmI0NGE2ZWEyMTk3MTNmYjFhODlhMjIuaHRtbCksIGFuIGFwcHJveGltYXRlIGVzdGltYXRvciBmb3IgdGhlIHZhcmlhbmNlIGlzCgokJApyID0gXGZyYWN7XGJhcnt5fX17XGJhcnt4fX0sIFx0ZXh0e3doZXJlfVwgXGJhcnt5fT1cZnJhY3sxfXtufVxzdW1fe2k9MX1ee259eV9pXCBcdGV4dHthbmR9XCBcYmFye3h9PVxmcmFjezF9e259XHN1bV97aT0xfV57bn14X2lcIFxcClxoYXR7Vn0ocikgPSAoMS1cZnJhY3tufXtOfSkoXGZyYWN7MX17XGJhcnt4fV4yfSlcZnJhY3tzX3JeMn17bn1cIFx0ZXh0e3doZXJlfVwgc19yXjI9XGZyYWN7MX17bi0xfVxzdW1fe2k9MX1ee259KHlfaS1yeF9pKV4yCiQkCgpBc3N1bWluZyB0aGF0IE5cPlw+biwgd2UgY2FuIGlnbm9yZSB0aGUgZmlyc3QgdGVybSBpbiB0aGUgdmFyaWFuY2UgZXN0aW1hdG9yLiBUaGUgcmVzdCBjYW4gYmUgY2FsY3VsYXRlZCBmcm9tIHRoZSBkYXRhCgpgYGB7cn0KZGF0c3VtIDwtIGRhdCAlPiUKICBmaWx0ZXIoIWlzLm5hKHBsYXNtaWQpKSAlPiUgCiAgZ3JvdXBfYnkocGxhc21pZCwgaG9zdCkgJT4lIAogIHN1bW1hcml6ZSgKICAgICBuID0gbigpLAogICAgbUcgPSBtZWFuKEJMMS5IKSwKICAgIG1SID0gbWVhbihZTDIuSCksCiAgICAgQSA9IG1lYW4oWUwyLkgvQkwxLkgpLAogICAgIHIgPSBtUi9tRywKICAgIHMyID0gMS8obi0xKSpzdW0oKFlMMi5IIC0gcipCTDEuSCleMiksCiAgICB2ciA9IDEvKG1HXjIpKnMyL24sCiAgICBzZSA9IHNxcnQodnIpLAogICAgLmdyb3VwcyA9ICJkcm9wIgogICkgJT4lIAogIHNlbGVjdCgtczIsIC1yLCAtdnIpIyAlPiUgCiAgI3Bpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBob3N0LCB2YWx1ZXNfZnJvbSA9IEJMMS5IOmBuUi9HYCkgJT4lIAogICNtdXRhdGUoYHBobzLiiIYvUEhPMmAgPSBgUi9HX3BobzLiiIZgL2BSL0dfUEhPMmAsCiAgIyAgICAgICBgbi5waG8y4oiGL1BITzJgID0gYG5SL0dfcGhvMuKIhmAvYG5SL0dfUEhPMmApCmBgYAoKRm9yIGVhY2ggY2hpbWVyYSwgd2Ugd291bGQgYWxzbyBsaWtlIHRvIGNhbGN1bGF0ZSAqKnRocmVlIHZhbHVlcyoqOgoKMS4gIEEgaW4gKnBobzLiiIYqOiB0aGlzIGlzIGl0cyBiYXNlIGFjdGl2aXR5IHdpdGhvdXQgUGhvMgoyLiAgQSBpbiAqUEhPMio6IHRoaXMgaXMgaXRzIGZ1bGwgYWN0aXZpdHkgd2l0aCBQaG8yCjMuICBBX1BITzIgLyBBX3BobzLiiIY6IHRoaXMgaXMgdGhlIFBobzIgZW5oYW5jZW1lbnQgb2YgYWN0aXZpdHkKCldlIGFzc2lnbiB0aGUgY2hpbWVyYXMgaW50byBzZXZlcmFsIGdyb3VwcywgYmFzZWQgb24gdGhlaXIgQV9QSE8yIGFuZCBBX1BITzIvQV9waG8y4oiGCgpgYGB7cn0KeGltZXJhIDwtIGRhdHN1bSAlPiUKICBwaXZvdF93aWRlcihpZF9jb2xzID0gcGxhc21pZCwgbmFtZXNfZnJvbSA9IGhvc3QsCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSBjKEEsIHNlKSkgJT4lIAogIG11dGF0ZSgKICAgIHJBX1BITzIgPSBBX1BITzIgLyBBX1BITzJbcGxhc21pZCA9PSAiMTk0Il0sCiAgICByQV9waG8yID0gQV9waG8yIC8gQV9waG8yW3BsYXNtaWQgPT0gIjE5NCJdLAogICAgYm9vc3QgPSBBX1BITzIgLyBBX3BobzIsCiAgICBncm91cCA9IGNhc2Vfd2hlbigKICAgICAgcGxhc21pZCAlaW4lIGMoIjE4OCIsICIxOTQiKSB+ICJyZWYiLAogICAgICByQV9QSE8yIDwgMC4yICAgICAgICAgICAgICAgIH4gIm4uZi4iLAogICAgICAuZGVmYXVsdCA9ICJjaGltZXJhIgogICAgKSwKICAgIGdyb3VwID0gZmN0X3JlbGV2ZWwoZ3JvdXAsICJyZWYiLCAiY2hpbWVyYSIsICJuLmYuIikKICApICU+JSAKICBsZWZ0X2pvaW4oc2VsZWN0KG1ldGEsIHBsYXNtaWQsIHNldCwgc3ltYm9sKSwgYnkgPSAicGxhc21pZCIpICU+JSAKICBtdXRhdGUoc3ltYm9sID0gZmN0X3Jlb3JkZXIoc3ltYm9sLCByQV9QSE8yLCAuZGVzYyA9IFRSVUUpKSAlPiUgCiAgcmVsb2NhdGUoYyhzZXQsIHN5bWJvbCwgZ3JvdXApLCAuYWZ0ZXIgPSBwbGFzbWlkKQpgYGAKCkV4cG9ydCB0aGUgc3VtbWFyaXplZCBkYXRhCgpgYGB7cn0Kd3JpdGVfdHN2KHhpbWVyYSwgZmlsZSA9ICIuLi9vdXRwdXQvMjAyMzExMjUtUEhPNXByLWNoaW1lcmEtc3VtbWFyaXplZC50c3YiKQpgYGAKClRvIGJlIGFibGUgdG8gcGxvdCBhbGwgdGhlIGRhdGEgcG9pbnRzLCBsZXQncyBnZW5lcmF0ZSBhbm90aGVyIGRhdGEgZnJhbWUgd2l0aCB0aGUgaW5kaXZpZHVhbCByYXRpb3MuCgpgYGB7cn0KZGF0X3NlcCA8LSBkYXQgJT4lCiAgZmlsdGVyKCFpcy5uYShwbGFzbWlkKSkgJT4lIAogIG11dGF0ZShBID0gWUwyLkgvQkwxLkgpICU+JSAKICBzZWxlY3QocGxhc21pZCwgaG9zdCwgQkwxLkgsIFlMMi5ILCBBLCBmbGFnKSAlPiUgCiAgbGVmdF9qb2luKHNlbGVjdChtZXRhLCBwbGFzbWlkLCBzZXQsIHN5bWJvbCksIGJ5ID0gInBsYXNtaWQiKSMgJT4lIAogICNtdXRhdGUoc3ltYm9sID0gZmN0X3Jlb3JkZXIoc3ltYm9sLCByQV9QSE8yLCAuZGVzYyA9IFRSVUUpKSMgJT4lIApgYGAKCiMgQW5hbHlzaXMKCiMjIFBsb3R0aW5nIGZ1bmN0aW9ucwoKPCEtLS0KU2V0IHVwIGNvbW1vbiBwYXJhbWV0ZXJzIGZvciB0aHJlc2hvbGRpbmcgYW5kIHBsb3R0aW5nCmBgYHtyfQojIHJlZmVyZW5jZSBQaG80IHBsYXNtaWQgaWRzCnJlZnMgPC0gYygiMTg4IiwgIjE5NCIpCiMgY29sb3JzCmRhdGUuY29sb3JzID0gYyhicmV3ZXIucGFsKG5hbWU9IkRhcmsyIiwgbiA9IDgpLCBicmV3ZXIucGFsKG5hbWU9IlBhaXJlZCIsIG4gPSA4KSkKaG9zdC5jb2xvcnMgPSBjKCJQSE8yIiA9ICJncmF5MzAiLCAicGhvMiIgPSAiZ3JheTcwIikKcG9pbnQuY29sb3JzID0gYygiUEhPMiIgPSAiZm9yZXN0Z3JlZW4iLCAicGhvMiIgPSAicHVycGxlNCIpCiMgCmBgYAotLS0+CgpTb3VyY2UgdGhlIHNjcmlwdHMKCmBgYHtyfQpzb3VyY2UoIi4uL3NjcmlwdC8yMDI0MDIxMS1jaGltZXJhLWRhdGEtcGxvdHRpbmctZnVuY3Rpb25zLlIiKQpgYGAKCk1vZGlmeSB0aGUgY29tcG9uZW50IHBsb3R0aW5nIGZ1bmN0aW9uIGZvciBzcGVjaWFsIHB1cnBvc2VzCgpgYGB7cn0KbXlfcGxvdF9yYXRpbyA8LSBmdW5jdGlvbihzZWxlY3Rpb24pewogICMgY3VzdG9tIGNvbG9ycyBmb3IgdGhpcyBmdW5jdGlvbgogIGRhdGUuY29sb3JzID0gYyhicmV3ZXIucGFsKG5hbWU9IkRhcmsyIiwgbiA9IDgpLCBicmV3ZXIucGFsKG5hbWU9IlBhaXJlZCIsIG4gPSA4KSkKICBob3N0LmNvbG9ycyA9IGMoIlBITzIiID0gImdyYXkzMCIsICJwaG8yIiA9ICJncmF5NzAiKQogIHBvaW50LmNvbG9ycyA9IGMoIlBITzIiID0gImZvcmVzdGdyZWVuIiwgInBobzIiID0gInB1cnBsZTQiKQogICMgcHJlcGFyZSBkYXRhCiAgdG1wIDwtIG15X2RhdGFfcHJlcChzZWxlY3Rpb24pCiAgIyBwbG90dGluZwogIHAgPC0gdG1wICU+JSAKICAgIHNlbGVjdCgtYyhGU0MuSCwgbkdGUCwgblJGUCwgZmxhZykpICU+JSAKICAgIG11dGF0ZShgUi9HYCA9IFlMMi5IL0JMMS5IKSAlPiUgCiAgICBwaXZvdF9sb25nZXIoY29scyA9IGMoQkwxLkgsIFlMMi5ILCBgUi9HYCksIAogICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInBhcmFtZXRlciIsIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JSAKICAgIG11dGF0ZShwYXJhbWV0ZXIgPSBmYWN0b3IocGFyYW1ldGVyLCBsZXZlbHMgPSBjKCJSL0ciLCAiWUwyLkgiLCAiQkwxLkgiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiUkZQL0dGUCIsICJQSE81cFJGUCIsICJQaG80LUdGUCIpKSkgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gc3ltYm9sLCB5ID0gdmFsdWUsIGdyb3VwID0gaG9zdCkpICsgCiAgICBzdGF0X3N1bW1hcnkoYWVzKGdyb3VwID0gaG9zdCksIGZ1bi5kYXRhID0gIm1lYW5fY2xfYm9vdCIsIGdlb20gPSAiZXJyb3JiYXIiLAogICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC41KSwgd2lkdGggPSAwLjMpICsKICAgIGdlb21fYmFyKGFlcyhmaWxsID0gaG9zdCksIHdpZHRoID0gMC41LCBhbHBoYSA9IDAuOCwKICAgICAgICAgICAgIHN0YXQgPSAic3VtbWFyeSIsIGZ1biA9ICJtZWFuIiwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjUpKSArCiAgICBnZW9tX3BvaW50KGRhdGEgPSBmdW5jdGlvbih4KSBzdWJzZXQoeCwgIXN5bWJvbCAlaW4lIGMoIkNDQ0NDIiwgIlNTU1NTIikpLAogICAgICAgICAgICAgICBhZXMoZ3JvdXAgPSBob3N0LCBjb2xvciA9IGRhdGUpLCBzaXplID0gMSwgc2hhcGUgPSAzLCBhbHBoYSA9IDAuOSwKICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXJkb2RnZShkb2RnZS53aWR0aCA9IDAuNSwgaml0dGVyLndpZHRoID0gMC4xKSkgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGRhdGUuY29sb3JzLCBndWlkZSA9ICJub25lIikgKwogICAgI2dlb21fcG9pbnQoZGF0YSA9IGZ1bmN0aW9uKHgpIHN1YnNldCh4LCAhc3ltYm9sICVpbiUgYygiQ0NDQ0MiLCAiU1NTU1MiKSksCiAgICAjICAgICAgICAgICBhZXMoZ3JvdXAgPSBob3N0LCBjb2xvciA9IGhvc3QpLCBzaXplID0gMSwgc2hhcGUgPSAzLCBhbHBoYSA9IDAuOSwKICAgICMgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyZG9kZ2UoZG9kZ2Uud2lkdGggPSAwLjUsIGppdHRlci53aWR0aCA9IDAuMSkpICsKICAgICNzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcG9pbnQuY29sb3JzKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBob3N0LmNvbG9ycykgKwogICAgZmFjZXRfZ3JpZChwYXJhbWV0ZXJ+Z3JvdXAsIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZV94IikgKwogICAgdGhlbWVfYncoYmFzZV9zaXplID0gMTgpICsgYmFja2dyb3VuZF9ncmlkKG1pbm9yID0gIm5vbmUiKSArIAogICAgeGxhYigiUGhvNCBjaGltZXJhIikgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAzMCwgaGp1c3QgPSAxLCBmYW1pbHkgPSAibW9ubyIpLAogICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQogIHJldHVybihwKSAKfQpgYGAKCiMjIFBobzQgY2hpbWVyYSBwcm90ZWluIGxldmVsIHZhcmlhdGlvbgoKV2hhdCBpcyB0aGUgZGlzdHJpYnV0aW9uIG9mIFBobzQgY2hpbWVyYSBwcm90ZWluIGxldmVscz8gSXMgdGhlIG1DaGVycnkvbU5lb24gcmF0aW8gYSBmYWl0aGZ1bCBtZWFzdXJlIG9mIHRoZSBjaGltZXJhJ3MgYWN0aXZpdGllcz8KCkRpc3RyaWJ1dGlvbiBvZiBQaG80LW1OZW9uIGxldmVscyBncm91cGVkIGJ5IHBsYXNtaWQgYW5kIGhvc3QuCgpgYGB7cn0KaG9zdC5sYWJlbHMgPSBjKCJQSE8yIiwgInBobzLiiIYiKQpwb2ludC5jb2xvcnMgPSBjKCJQSE8yIiA9ICJmb3Jlc3RncmVlbiIsICJwaG8yIiA9ICJwdXJwbGU0IikKcDEgPC0gZGF0ICU+JSAKICBmaWx0ZXIoIWlzLm5hKHBsYXNtaWQpKSAlPiUgCiAgbXV0YXRlKHBsYXNtaWQgPSBmY3RfcmVvcmRlcihwbGFzbWlkLCBCTDEuSCwgLmZ1biA9IG1lZGlhbikgJT4lIAogICAgICAgICAgIGZjdF9yZWxldmVsKCIxOTQiLCAiMTg4IikpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBwbGFzbWlkLCB5ID0gQkwxLkgpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBob3N0KSwgcG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIoMC4xKSwKICAgICAgICAgICAgIHNpemUgPSAxLjEpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKCJIb3N0IiwgdmFsdWVzID0gcG9pbnQuY29sb3JzLCBsYWJlbHMgPSBob3N0LmxhYmVscykgKwogIHNjYWxlX3lfbG9nMTAoYnJlYWtzID0gYygxMDAsIDEwMDAsIDEwMDAwKSwgZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSAwLjEpKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IDAuMDMpKSArCiAgeGxhYigiUGhvNCBjb25zdHJ1Y3RzIikgKyB5bGFiKCJQaG80LW1OZW9uIChhLnUuKSIpICsKICB0aGVtZV9jb3dwbG90KCkgKyBwYW5lbF9ib3JkZXIoY29sb3IgPSAiZ3JheTMwIiwgc2l6ZSA9IDEuMikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSByZWwoMC42KSwgdmp1c3QgPSAwLjUpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC44KSksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuOSkpLAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuMDUsIDAuOSksCiAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChmYWNlID0gMykpCnAxCiNnZ3NhdmUoIi4uL2ltZy8yMDI0MDMwNy1QaG80LWNoaW1lcmEtcHJvdGVpbi1sZXZlbC12YXJpYXRpb24ucG5nIiwgCiMgICAgICAgd2lkdGggPSA2LCBoZWlnaHQgPSAzKQpgYGAKCkFsbCBjaGltZXJhIChTY1BobzQgYW5kIENnUGhvNCByZW1vdmVkKSBwcm90ZWluIGxldmVscyBieSBob3N0CgpgYGB7cn0KdG1wIDwtIGRhdCAlPiUgCiAgZmlsdGVyKHBsYXNtaWQgPT0gIjE5NCIsIGhvc3QgPT0gIlBITzIiKQoKbG0gPC0gbG0oWUwyLkggfiBCTDEuSCwgZGF0YSA9IHRtcCkKc3VtbWFyeShsbSkKCnAyIDwtIHRtcCAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gQkwxLkgsIHkgPSBZTDIuSCkpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsgCiAgc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9IHkgfiB4KSArCiAgeGxhYigiUGhvNC1tTmVvbiIpICsgeWxhYigiUEhPNXByLW1DaGVycnkiKSArCiAgdGhlbWVfY293cGxvdCgpICsgCiAgcGFuZWxfYm9yZGVyKGNvbG9yID0gImdyYXkzMCIsIHNpemUgPSAxLjIpICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDEuMikpCiAgKQoKcDIKZ2dzYXZlKCIuLi9pbWcvMjAyNDAzMDctU2NQaG80LW1DaGVycnktdnMtbU5lb24tY29uc2lzdGVudC5wbmciLCAKICAgICAgIHdpZHRoID0gNCwgaGVpZ2h0ID0gMy41KQpgYGAKCmBgYHtyfQpob3N0LmNvbG9ycyA9ICBjKCJQSE8yIiA9ICJncmF5NjAiLCAicGhvMiIgPSAib3JhbmdlIikKCnRtcCA8LSBkYXQgJT4lIAogIGZpbHRlcihwbGFzbWlkICVpbiUgYygiMTg4IiwgIjE5NCIpLCAKICAgICAgICAgZGF0ZSAlaW4lIGMoIjAyLzA4IiwgIjAyLzExIiwgIjAyLzE4IiwgIjAyLzIxIiwgIjAyLzIzIiwgIjAzLzMxIikpICU+JSAKICBtdXRhdGUoQSA9IFlMMi5IL0JMMS5ILAogICAgICAgICBQaG80ID0gZmFjdG9yKHBsYXNtaWQsIGxldmVscyA9IGMoIjE5NCIsICIxODgiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiU2NQaG80IiwgIkNnUGhvNCIpKSkgCnAzIDwtIHRtcCAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSwgeSA9IEEpKSArIAogIGdlb21fYmFyKGFlcyhmaWxsID0gaG9zdCksIHN0YXQgPSAic3VtbWFyeSIsIGZ1biA9ICJtZWFuIiwgCiAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjkpLCBhbHBoYSA9IDAuOSkgKwogIGdlb21fcG9pbnQoYWVzKGdyb3VwID0gaG9zdCksIHNpemUgPSAwLjYsIHNoYXBlID0gMywKICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjkpKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKCJIb3N0IiwgdmFsdWVzID0gaG9zdC5jb2xvcnMsIGxhYmVscyA9IGhvc3QubGFiZWxzKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSAxOjYpICsKICAjc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gIm1lYW5fc2UiLCBnZW9tID0gInBvaW50cmFuZ2UiLCBjb2xvciA9ICJyZWQiKSArCiAgZmFjZXRfZ3JpZChQaG80IH4gLikgKwogIHlsYWIoIm1DaGVycnkvbU5lb24iKSArIHhsYWIoIlJlcGxpY2F0ZSIpICsKICB0aGVtZV9jb3dwbG90KCkgKyBwYW5lbF9ib3JkZXIoY29sb3IgPSAiZ3JheTMwIiwgc2l6ZSA9IDEuMikrCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC43KSksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDEpKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjkpKSwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuOCksIGZhY2UgPSAzKSkKcDMKI2dnc2F2ZSgiLi4vaW1nLzIwMjQwMzA3LUNnUGhvNC1tQ2hlcnJ5LXZzLW1OZW9uLWNvbnNpc3RlbnQucG5nIiwgCiMgICAgICAgd2lkdGggPSA0LCBoZWlnaHQgPSAzLjIpCgojIHNhbXBsZSBzaXplIHBlciBkYXkgb2YgZXhwZXJpbWVudAp0bXAgJT4lIGNvdW50KGRhdGUsIFBobzQsIGhvc3QpICU+JSAKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gaG9zdCwgdmFsdWVzX2Zyb20gPSBuKQpgYGAKCiMjIEhpZ2ggdmFyaWFuY2Ugc2FtcGxlcwoKU3VtbWFyaXplIHRoZSBiYWNrZ3JvdW5kIHN1YnRyYWN0ZWQgZGF0YSBieSBjYWxjdWxhdGluZyB0aGUgbWVhbnMgYW5kIGN2IGZvciBlYWNoIHN0cmFpbi4KCmBgYHtyfQpjdiA8LSBkYXQgJT4lIAogIHNlbGVjdCgtbkdGUCwgLW5SRlApICU+JQogIHBpdm90X2xvbmdlcihGU0MuSDpZTDIuSCwgbmFtZXNfdG8gPSAicGFyYW1ldGVyIiwgdmFsdWVzX3RvID0gImludGVuc2l0eSIpICU+JSAKICBncm91cF9ieShkYXRlLCBwbGFzbWlkLCBob3N0LCBwYXJhbWV0ZXIpICU+JSAKICBzdW1tYXJpemUoCiAgICBuID0gbigpLAogICAgbWVhbiA9IG1lYW4oaW50ZW5zaXR5KSwKICAgIGN2ID0gc2QoaW50ZW5zaXR5KS9tZWFuKGludGVuc2l0eSksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKSAlPiUgCiAgYXJyYW5nZShkZXNjKGN2KSkKYGBgCgpVc2UgdGhlIGNvbnRyb2wgc3RyYWluIChwSDE5NCB3aXRoIFBITzIpIHRvIGlkZW50aWZ5IGFuZCBjb3JyZWN0IGZvciBzeXN0ZW1hdGljIGJpYXNlcwoKYGBge3J9CmNvbnRyb2wgPC0gZmlsdGVyKGRhdCwgcGxhc21pZCA9PSAiMTk0IiwgaG9zdCA9PSAiUEhPMiIpICU+JSAKICBzZXBhcmF0ZSh3ZWxsLCBpbnRvID0gYygicm93IiwgImNvbCIpLCBzZXAgPSAxKSAlPiUgCiAgZHJvcGxldmVscygpCmBgYAoKTW9kZWwgZm9yIG1OZW9uCgpgYGB7cn0KZ2ZwLm1vZGVsLjAgPC0gbG0oQkwxLkggfiBsb2cxMChldmVudHMpICsgZGF0ZSArIHJvdypjb2wsIGRhdGEgPSBjb250cm9sKQpzdGVwKGdmcC5tb2RlbC4wKQpnZnAubW9kZWwuMSA8LSBsbShCTDEuSCB+IGRhdGUgKyBjb2wsIGRhdGEgPSBjb250cm9sKQpgYGAKCk1vZGVsIGZvciBQSE81cHI6OlJGUAoKYGBge3J9CnJmcC5tb2RlbC4wIDwtIGxtKFlMMi5IIH4gbG9nMTAoZXZlbnRzKSArIGRhdGUgKyByb3cqY29sLCBkYXRhID0gY29udHJvbCkKc3RlcChyZnAubW9kZWwuMCkKcmZwLm1vZGVsLjEgPC0gbG0oWUwyLkggfiBsb2cxMChldmVudHMpICsgZGF0ZSArIHJvdyArIGNvbCwgZGF0YSA9IGNvbnRyb2wpCmBgYAoKPiB0aGVyZSBhcmUgbW9yZSBzeXN0ZW1hdGljIHNoaWZ0cyBpbiB0aGUgUkZQLCBzaWduaWZpY2FudCBmb3Igcm93LCBjb2wsIGRhdGUgYW5kIGFsc28gXCMgb2YgZXZlbnRzIGhvd2V2ZXIsIEkgd29uJ3QgYmUgcmVtb3ZpbmcgdGhlc2UgZWZmZWN0cyB5ZXQsIGJlY2F1c2UgSSd2ZSBmb3VuZCB0aGF0IFJGUC9HRlAgcmF0aW9zIGFyZSBwcmV0dHkgY29uc2lzdGVudCBhY3Jvc3MgZGF5cy4gSW4gb3RoZXIgd29yZHMsIHRoZSB2YXJpYXRpb24gaW4gR0ZQIGFuZCBSRlAgbWF5IGJlIGNhbmNlbGxlZCBvdXQuCgpDaGVjayBmb3IgZWFjaCBwbGFzbWlkIGhvdyBjb25zaXN0ZW50IGFyZSB0aGUgbWVhc3VyZW1lbnRzIGJldHdlZW4gZGF5cwoKYGBge3J9CnRtcCA8LSBkYXQgJT4lIAogICMgcmVtb3ZlIG9uZSBzYW1wbGUgd2l0aCBvbmx5IG9uZSB2YWxpZCBkYXkgb2YgZXhwZXJpbWVudAogIGZpbHRlcighKHBsYXNtaWQgPT0gIjIxOCIgJiBob3N0ID09ICJQSE8yIiksICFwbGFzbWlkICVpbiUgYygiMTg4IiwgIjE5NCIsIE5BKSkgJT4lIAogIG5lc3QoZGF0YSA9IGMoZGF0ZSwgQkwxLkgsIFlMMi5IKSwgLmJ5ID0gYyhwbGFzbWlkLCBob3N0KSkKCmRheS52YXIuZ2ZwIDwtIHRtcCAlPiUgCiAgbXV0YXRlKG1vZGVsID0gbWFwKGRhdGEsIGZ1bmN0aW9uKGRmKSBsbShCTDEuSCB+IGRhdGUsIGRhdGEgPSBkZikpLAogICAgICAgICB0aWRpZWQgPSBtYXAobW9kZWwsIGJyb29tOjp0aWR5KSkgJT4lIAogIHVubmVzdCh0aWRpZWQpICU+JSAKICBmaWx0ZXIodGVybSAhPSAiKEludGVyY2VwdCkiKSAlPiUgCiAgbXV0YXRlKHAuYWRqID0gcC5hZGp1c3QocC52YWx1ZSwgbWV0aG9kID0gIkJIIikpICU+JSAKICBzZWxlY3QoLWRhdGEsIC1tb2RlbCkgJT4lIAogIGZpbHRlcihwLmFkaiA8IDAuMTApICU+JSAKICBhcnJhbmdlKHBsYXNtaWQsIGhvc3QpCgpkYXkudmFyLnJmcCA8LSB0bXAgJT4lIAogIG11dGF0ZShtb2RlbCA9IG1hcChkYXRhLCBmdW5jdGlvbihkZikgbG0oWUwyLkggfiBkYXRlLCBkYXRhID0gZGYpKSwKICAgICAgICAgdGlkaWVkID0gbWFwKG1vZGVsLCBicm9vbTo6dGlkeSkpICU+JSAKICB1bm5lc3QodGlkaWVkKSAlPiUgCiAgZmlsdGVyKHRlcm0gIT0gIihJbnRlcmNlcHQpIikgJT4lIAogIG11dGF0ZShwLmFkaiA9IHAuYWRqdXN0KHAudmFsdWUsIG1ldGhvZCA9ICJCSCIpKSAlPiUgCiAgc2VsZWN0KC1kYXRhLCAtbW9kZWwpICU+JSAKICBmaWx0ZXIocC5hZGogPCAwLjEwKSAlPiUgCiAgYXJyYW5nZShwbGFzbWlkLCBob3N0KQpgYGAKCmBgYHtyfQojIGV4dHJhY3QgeGltZXJhIG5hbWVzCnJlZnMgPC0gYygiMTg4IiwiMTk0IikKIyBtYWtlIGEgdGVzdCBzZXQKZGF5LnZhci5nZnAubGlzdCA8LSB1bmlxdWUoZGF5LnZhci5nZnAkcGxhc21pZCkKZGF5LnZhci5yZnAubGlzdCA8LSB1bmlxdWUoZGF5LnZhci5yZnAkcGxhc21pZCkKYGBgCgpIaWdoIGRheS10by1kYXkgR0ZQIHZhcmlhbmNlOiBgciBkYXkudmFyLmdmcC5saXN0YCBIaWdoIGRheS10by1kYXkgUkZQIHZhcmlhbmNlOiBgciBkYXkudmFyLnJmcC5saXN0YAoKUGxvdHRpbmcgY29tcG9uZW50cyBmb3IgY2hpbWVyYXMgd2l0aCBoaWdoIGRheS10by1kYXkgdmFyaWFuY2UgaW4gUGhvNC1tTmVvbgoKYGBge3J9CnAgPC0gbXlfcGxvdF9yYXRpbyhjKHJlZnMsZGF5LnZhci5nZnAubGlzdCkpIyArIApwCmBgYAoKPiBXYXRjaCBvdXQgZm9yIENTQ3NjQywgU0NDc1MsIFNDQ3NTCgpQbG90dGluZyBjb21wb25lbnRzIGZvciBjaGltZXJhcyB3aXRoIGhpZ2ggZGF5LXRvLWRheSB2YXJpYW5jZSBpbiAqUEhPNXByKi1tQ2hlcnJ5CgpgYGB7cn0KcCA8LSBteV9wbG90X3JhdGlvKGMocmVmcyxkYXkudmFyLnJmcC5saXN0KSkKcApgYGAKCj4gbW9zdCBvZiB0aGUgZGF5LXRvLWRheSB2YXJpYW5jZSBhcmUgY2FuY2VsZWQgb3V0IGFmdGVyIFJGUC9HRlAgbm9ybWFsaXphdGlvbgoKIyMgQWxsIGNoaW1lcmEsIHNjYXR0ZXIgcGxvdAoKUGxvdCBhbGwgY2hpbWVyYXMsIGNvbG9yaW5nIGJhc2VkIG9uIFAySUQgc291cmNlCmBgYHtyfQpteV9zY2F0dGVyX3Bsb3RfZml4IDwtIGZ1bmN0aW9uKCl7CiAgIyB0aGlzIGZ1bmN0aW9uIGlzIHRoZSBzYW1lIGFzIHRoZSBvbmUgaW4gdGhlIHNjcmlwdCBmaWxlLCBidXQgaXMgdXNlZCB0byAKICAjIHBsb3QgcmVnaW9uIDQgZWZmZWN0cyBhbG9uZSwgYW5kIGRvZXNuJ3QgdGFrZSBhbnkgaW5wdXQKICBzMSA9IG15X2RhdGFfc2VsZWN0KHBhdHRlcm4gPSAiWFhYQ0NYIikKICBzMiA9IG15X2RhdGFfc2VsZWN0KHBhdHRlcm4gPSAiWFhYU1NYIikKICBzY2F0dGVyLmNvbG9ycyA9IGMoIlNjUGhvNCIgPSAiZm9yZXN0Z3JlZW4iLCAiQ2dQaG80IiA9ICJibHVlMyIsIAogICAgICAgICAgICAgICAgICAgICAiUDJJRDpDZyIgPSAiZGVlcHNreWJsdWUiLCAiUDJJRDpTYyIgPSAicGFsZWdyZWVuMiIsCiAgICAgICAgICAgICAgICAgICAgICJQMklEOm1peGVkIiA9ICJncmF5MjAiKQogIHNjYXR0ZXIuc2l6ZSA9IGMoIlNjUGhvNCIgPSAzLjUsICJDZ1BobzQiID0gMy41LAogICAgICAgICAgICAgICAgICAgICAiUDJJRDpDZyIgPSAyLjUsICJQMklEOlNjIiA9IDIuNSwgIlAySUQ6bWl4ZWQiID0gMi41KQogIHAgPC0geGltZXJhICU+JSAKICAgICMgZXhjbHVkZSB0aGUgYWx0ZXJuYXRpdmUgYnJlYWsgcG9pbnQgc2V0cyAiQSIgYW5kICJCIgogICAgIyBpbiBwYXJ0aWN1bGFyLCBwSDI5NCBpcyBhbiBhbHRlcm5hdGl2ZSBicmVhayBwb2ludCBDQ0Njcywgd2hlcmUgUDJJRDpDZwogICAgIyBleHRlbmRzIHRvIGFhIDI3MCBpbnN0ZWFkIG9mIDQ1OC4gaXQgaGFzIG5lYXJseSB0aGUgc2FtZSBhY3Rpdml0aWVzIGFzCiAgICAjIENnUGhvNC4gSG93ZXZlciwgdGhlIENDQ0NTIGluIHRoZSBtYWluIHNldCBoYXMgc2lnbmlmaWNhbnRseSByZWR1Y2VkCiAgICAjIGFjdGl2aXRpZXMgYm90aCB3aXRoIGFuZCB3aXRob3V0IFBobzQuIFdlIGxhdGVyIHRlc3RlZCB3aGV0aGVyIHRoZSAKICAgICMgYWRkaXRpb25hbCBQMklEOkNnNCByZXNjdWVzIHRoZSBlZmZlY3QgKHNlZSBDZzRleHQgYmVsb3cpIGFuZCBpdCBkaWRuJ3QKICAgICMgc28gZmFyLCB0aGlzIHNlZW1zIHRvIGJlIGFuIG9uZS1vZmYuIHdlIG5lZWQgdG8gZnVydGhlciBpbnZlc3RpZ2F0ZSBpdHMKICAgICMgYWN0aXZpdGllcy4KICAgIGZpbHRlcihzZXQgJWluJSBjKCJNIiwgIlMiKSkgJT4lIAogICAgbXV0YXRlKEFfUEhPMiA9IHNpZ25pZihBX1BITzIsIGRpZ2l0cyA9IDIpLAogICAgICAgICAgIEFfcGhvMiA9IHNpZ25pZihBX3BobzIsIGRpZ2l0cyA9IDIpLAogICAgICAgICAgIGdyb3VwID0gY2FzZV93aGVuKAogICAgICAgICAgICAgc3ltYm9sID09ICJDQ0NDQyIgfiAiQ2dQaG80IiwKICAgICAgICAgICAgIHN5bWJvbCA9PSAiU1NTU1MiIH4gIlNjUGhvNCIsCiAgICAgICAgICAgICBwbGFzbWlkICVpbiUgczEgfiAiUDJJRDpDZyIsCiAgICAgICAgICAgICBwbGFzbWlkICVpbiUgczIgfiAiUDJJRDpTYyIsCiAgICAgICAgICAgICAuZGVmYXVsdCA9ICJQMklEOm1peGVkIgogICAgICAgICAgICksCiAgICAgICAgICAgZ3JvdXAgPSBmY3RfcmVsZXZlbChncm91cCwgbmFtZXMoc2NhdHRlci5jb2xvcnMpKSkgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gQV9QSE8yLCB5ID0gQV9waG8yLCBsYWJlbCA9IHN5bWJvbCkpICsgCiAgICBnZW9tX2FibGluZShzbG9wZSA9IDEpICsKICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZ3JvdXAsIHNpemUgPSBncm91cCkpICsgCiAgICBzY2FsZV9jb2xvcl9tYW51YWwoTlVMTCwgdmFsdWVzID0gc2NhdHRlci5jb2xvcnMpICsKICAgIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcyA9IHNjYXR0ZXIuc2l6ZSwgZ3VpZGUgPSAibm9uZSIpICsKICAgIGxhYnMoeCA9IGJxdW90ZShBW1BITzJdKSwgeSA9IGJxdW90ZShBW3BobzIqRGVsdGFdKSkgKwogICAgdGhlbWVfY293cGxvdCgpICsgcGFuZWxfYm9yZGVyKGNvbG9yID0gImdyYXkzMCIsIHNpemUgPSAxLjIpICsKICAgIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC44KSksCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuMDMsIDAuODMpLAogICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gMiwgc2l6ZSA9IHJlbCgxLjIpKSwKICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSkKICByZXR1cm4ocCkKfQpgYGAKCmBgYHtyfQpwIDwtIG15X3NjYXR0ZXJfcGxvdF9maXgoKQpnZ3NhdmUoZmlsZW5hbWUgPSAiLi4vaW1nLzIwMjQwMzA4LWFsbC1jaGltZXJhLXNjYXR0ZXItY29sb3ItYnktUDJJRC5wbmciLAogICAgICAgcGxvdCA9IHAsIHdpZHRoID0gNC41LCBoZWlnaHQgPSA0LCBkcGkgPSAzMDApCmdncGxvdGx5KHAgKyBsYWJzKHggPSAiQTxzdWI+UEhPMjwvc3ViPiIsIHkgPSAiQTxzdWI+cGhvMjwvc3ViPiIpICsKICAgICAgICAgICB0aGVtZV9ncmF5KGJhc2Vfc2l6ZSA9IDE2KSArCiAgICAgICAgICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X21hcmtkb3duKCkpLCAKICAgICAgICAgdG9vbHRpcCA9IGMoImxhYmVsIiwgIngiLCAieSIpKQpgYGAKCnRoaXMgZnVuY3Rpb24gaXMgdGhlIHNhbWUgYXMgbXlfc2NhdHRlcl9wbG90X2ZpeCBleGNlcHQgdGhhdCBpdCBwbG90cyBhbGwgdGhlIGNoaW1lcmFzIHdpdGhvdXQgY29sb3JpbmcgdGhlbSBkaWZmZXJlbnRseS4gZm9yIGZpZ3VyZSA1CmBgYHtyfQpteV9zY2F0dGVyX3Bsb3RfYWxsIDwtIGZ1bmN0aW9uKCl7CiAgIyB0aGlzIGZ1bmN0aW9uIGlzIHRoZSBzYW1lIGFzIG15X3NjYXR0ZXJfcGxvdF9maXggZXhjZXB0IHRoYXQgaXQgcGxvdHMgYWxsIHRoZSBjaGltZXJhcwogICMgd2l0aG91dCBjb2xvcmluZyB0aGVtIGRpZmZlcmVudGx5LiBmb3IgZmlndXJlIDUKICBzMSA9IG15X2RhdGFfc2VsZWN0KHBhdHRlcm4gPSAiWFhYQ0NYIikKICBzMiA9IG15X2RhdGFfc2VsZWN0KHBhdHRlcm4gPSAiWFhYU1NYIikKICBzY2F0dGVyLmNvbG9ycyA9IGMoIlNjUGhvNCIgPSAiZm9yZXN0Z3JlZW4iLCAiQ2dQaG80IiA9ICJibHVlMyIsIAogICAgICAgICAgICAgICAgICAgICAiUDJJRDpDZyIgPSAiZ3JheTIwIiwgIlAySUQ6U2MiID0gImdyYXkyMCIsCiAgICAgICAgICAgICAgICAgICAgICJQMklEOm1peGVkIiA9ICJncmF5MjAiKQogIHNjYXR0ZXIuc2l6ZSA9IGMoIlNjUGhvNCIgPSAzLjUsICJDZ1BobzQiID0gMy41LAogICAgICAgICAgICAgICAgICAgIlAySUQ6Q2ciID0gMi41LCAiUDJJRDpTYyIgPSAyLjUsICJQMklEOm1peGVkIiA9IDIuNSkKICBwIDwtIHhpbWVyYSAlPiUgCiAgICBmaWx0ZXIoc2V0ICVpbiUgYygiTSIsICJTIikpICU+JSAKICAgIG11dGF0ZShBX1BITzIgPSBzaWduaWYoQV9QSE8yLCBkaWdpdHMgPSAyKSwKICAgICAgICAgICBBX3BobzIgPSBzaWduaWYoQV9waG8yLCBkaWdpdHMgPSAyKSwKICAgICAgICAgICBncm91cCA9IGNhc2Vfd2hlbigKICAgICAgICAgICAgIHN5bWJvbCA9PSAiQ0NDQ0MiIH4gIkNnUGhvNCIsCiAgICAgICAgICAgICBzeW1ib2wgPT0gIlNTU1NTIiB+ICJTY1BobzQiLAogICAgICAgICAgICAgcGxhc21pZCAlaW4lIHMxIH4gIlAySUQ6Q2ciLAogICAgICAgICAgICAgcGxhc21pZCAlaW4lIHMyIH4gIlAySUQ6U2MiLAogICAgICAgICAgICAgLmRlZmF1bHQgPSAiUDJJRDptaXhlZCIKICAgICAgICAgICApLAogICAgICAgICAgIGdyb3VwID0gZmN0X3JlbGV2ZWwoZ3JvdXAsIG5hbWVzKHNjYXR0ZXIuY29sb3JzKSkpICU+JSAKICAgIGdncGxvdChhZXMoeCA9IEFfUEhPMiwgeSA9IEFfcGhvMiwgbGFiZWwgPSBzeW1ib2wpKSArIAogICAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxKSArCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGdyb3VwLCBzaXplID0gZ3JvdXApKSArIAogICAgc2NhbGVfY29sb3JfbWFudWFsKE5VTEwsIHZhbHVlcyA9IHNjYXR0ZXIuY29sb3JzKSArCiAgICBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXMgPSBzY2F0dGVyLnNpemUsIGd1aWRlID0gIm5vbmUiKSArCiAgICBsYWJzKHggPSBicXVvdGUoQVtQSE8yXSksIHkgPSBicXVvdGUoQVtwaG8yXSkpICsKICAgIHRoZW1lX2Nvd3Bsb3QoKSArIHBhbmVsX2JvcmRlcihjb2xvciA9ICJncmF5MzAiLCBzaXplID0gMS4yKSArCiAgICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuOCkpLAogICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gMiwgc2l6ZSA9IHJlbCgxLjIpKSwKICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSkKCiAgcmV0dXJuKHApCn0KYGBgCgpQbG90IGFsbCBjaGltZXJhcywgZm9yIEZpZy4gNQpgYGB7cn0KcCA8LSBteV9zY2F0dGVyX3Bsb3RfYWxsKCkKZ2dzYXZlKGZpbGVuYW1lID0gIi4uL2ltZy8yMDI0MTEyMS1hbGwtY2hpbWVyYS1zY2F0dGVyLnBuZyIsCiAgICAgICBwbG90ID0gcCwgd2lkdGggPSA0LjUsIGhlaWdodCA9IDQsIGRwaSA9IDMwMCkKZ2dwbG90bHkocCArIGxhYnMoeCA9ICJBPHN1Yj5QSE8yPC9zdWI+IiwgeSA9ICJBPHN1Yj5waG8yPC9zdWI+IikgKwogICAgICAgICAgIHRoZW1lX2dyYXkoYmFzZV9zaXplID0gMTYpICsKICAgICAgICAgICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfbWFya2Rvd24oKSksIAogICAgICAgICB0b29sdGlwID0gYygibGFiZWwiLCAieCIsICJ5IikpCmBgYAojIyBTcG90bGlnaHQgaW5kaXZpZHVhbCBjaGltZXJhcwoKVGhlIGdvYWwgaGVyZSBpcyB0byBwbG90IGluZGl2aWR1YWwgY2hpbWVyYXMgaW4gb3JkZXIgdG8gdGVzdCBzcGVjaWZpYyBoeXBvdGhlc2VzIGFuZCBtYWtlIGNlcnRhaW4gcG9pbnRzLgoKMS4gIFdlIHNlcGFyYXRlbHkgdGVzdGVkIGFuZCBmb3VuZCB0aGF0IENnUGhvNCBEQkQgYmluZHMgdGhlIGNvbnNlbnN1cyBETkEgbW9yZSBzdHJvbmdseSB0aGFuIFNjUGhvNCBkb2VzLCBhbmQgaXQgYWxzbyBoYXMgdHdvIGFkZGl0aW9uYWwgYWN0aXZhdGlvbiBib29zdGVyIHJlZ2lvbnMsIHdoaWNoIGVuaGFuY2UgdGhlIGFjdGl2aXR5IG9mIHRoZSBtYWluIEFELiBXZSB0aGVyZWZvcmUgaHlwb3RoZXNpemUgdGhhdCBieSByZXBsYWNpbmcgdGhlIGNvcnJlc3BvbmRpbmcgcmVnaW9ucyBpbiBTY1BobzQgd2l0aCB0aGUgcGFydHMgZnJvbSBDZ1BobzQsIHdlIHdvdWxkIGNyZWF0ZSBhIGNoaW1lcmljIFRGIHRoYXQgaXMgbm90IG9yIGZhciBsZXNzIGRlcGVuZGVudCBvbiBQaG8yLgoyLiAgV2UgYWxzbyBleHBlY3QgdGhhdCB0aG9zZSByZWdpb25zIGFkZGl0aXZlbHkgY29udHJpYnV0ZSB0byB0aGUgcmVkdWNlZCBQaG8yLWRlcGVuZGVuY2UsIHNob3duIGFzIGluY3JlYXNlZCBURiBhY3Rpdml0eSBvZiB0aGUgY2hpbWVyYSBpbiB0aGUgKnBobzLiiIYqIGJhY2tncm91bmQuCgpEZXNpZ24gcGxvdAoKYGBge3J9Cm15X3Bsb3Rfc3Vic2V0X3hpbWVyYSA8LSBmdW5jdGlvbihzeW1ib2xzKXsKICAjIHRoaXMgZnVuY3Rpb24gcGxvdHMgYSBzdWJzZXQgb2YgdGhlIGNoaW1lcmFzIGFzIGhvcml6b250YWwgYmFyIHBsb3RzCiAgIyBzaG93aW5nIHRoZSBSZWwuIEFfUEhPMiBhbmQgJUFfcGhvMuKIhiB2YWx1ZXMKICAjIGl0IHRha2VzIGFzIGlucHV0IGEgdmVjdG9yIGNvbnRhaW5pbmcgdGhlIHN5bWJvbHMgZm9yIHRoZSBjaGltZXJhcyBmb3IgCiAgIyBwbG90dGluZy4gdGhlIG9yZGVyIGluIHRoZSB2ZWN0b3IgZGV0ZXJtaW5lcyB0aGUgcGxvdCBvcmRlcgogICMgdGhlIGVuZG9nZW5vdXMgU2NQaG80IGFuZCBDZ1BobzQgYXJlIGltcGxpZWQKICBtaXNzaW5nIDwtIHNldGRpZmYoc3ltYm9scywgeGltZXJhJHN5bWJvbCkKICBpZihsZW5ndGgobWlzc2luZykgIT0gMCkKICAgIHN0b3AocGFzdGUobWlzc2luZywgImFyZSBub3QgZm91bmQiLCBzZXAgPSAiICIpKQogIAogIHRtcCA8LSBmaWx0ZXIoeGltZXJhLCBzeW1ib2wgJWluJSBjKCJTU1NTUyIsICJDQ0NDQyIsIHN5bWJvbHMpKSAlPiUgCiAgICBtdXRhdGUoCiAgICAgIHJTRV9QSE8yID0gc2VfUEhPMiAvIEFfUEhPMltzeW1ib2wgPT0gIlNTU1NTIl0sCiAgICAgIHJTRV9waG8yID0gc2VfcGhvMiAvIEFfcGhvMltzeW1ib2wgPT0gIlNTU1NTIl0KICAgICkgJT4lIAogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKHJBX1BITzIsIHJBX3BobzIsIHJTRV9QSE8yLCByU0VfcGhvMiksIAogICAgICAgICAgICAgICAgICNwaXZvdF9sb25nZXIoY29scyA9IGMoQV9QSE8yLCBBX3BobzIsIHNlX1BITzIsIHNlX3BobzIpLCAKICAgICAgICAgICAgICAgICBuYW1lc190byA9IGMoIi52YWx1ZSIsICJwYXJhbWV0ZXIiKSwgbmFtZXNfc2VwID0gIl8iLAogICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JSAKICAgIG11dGF0ZShwYXJhbWV0ZXIgPSBmY3RfcmVsZXZlbChwYXJhbWV0ZXIsICJQSE8yIiksCiAgICAgICAgICAgc3ltYm9sID0gZmFjdG9yKHN5bWJvbCwgbGV2ZWxzID0gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW5pcXVlKGMoIlNTU1NTIiwgIkNDQ0NDIiwgc3ltYm9scykpKSkgJT4lIAogICAgc2VsZWN0KC1jKEFfUEhPMjpib29zdCkpCiAgCiAgIyBsYWJlbGxlcgogIHBhci5leHBsYWluIDwtIGMoCiAgICBQSE8yID0gIlJlbC4gQTxzdWI+UEhPMjwvc3ViPiIsCiAgICAjYm9vc3QgPSAiQm9vc3QiLAogICAgcGhvMiA9ICJSZWwuIEE8c3ViPnBobzLiiIY8L3N1Yj4iCiAgKQogIAogIHAgPC0gZ2dwbG90KHRtcCwgYWVzKHkgPSBzeW1ib2wsIHggPSByQSkpICsKICAgIGdlb21fY29sKHdpZHRoID0gMC41LCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZ3JheTgwIikgKwogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMSwgbGluZXR5cGUgPSAyLCBjb2xvciA9ICJncmF5MzAiKSArCiAgICBnZW9tX2Vycm9yYmFyKGFlcyh4bWluID0gckEgLSByU0UsIHhtYXggPSByQSArIHJTRSksIHdpZHRoID0gMC4yKSArCiAgICBmYWNldF93cmFwKH5wYXJhbWV0ZXIsIHNjYWxlcyA9ICJmcmVlX3giLCMgc3dpdGNoID0gIngiLAogICAgICAgICAgICAgIGxhYmVsbGVyID0gbGFiZWxsZXIocGFyYW1ldGVyID0gcGFyLmV4cGxhaW4pKSArCiAgICBzY2FsZV95X2Rpc2NyZXRlKGxpbWl0cyA9IHJldikgKyAKICAgIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMC4wMiwgMC4wNSkpKSArCiAgICB0aGVtZV9jb3dwbG90KCkgKyBwYW5lbF9ib3JkZXIoY29sb3IgPSAiZ3JheTMwIikgKwogICAgYmFja2dyb3VuZF9ncmlkKG1ham9yID0gInkiLCBtaW5vciA9ICJub25lIikgKwogICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gImNvdXJpZXIiKSwKICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBzdHJpcC5wbGFjZW1lbnQgPSAib3V0c2lkZSIsCiAgICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfbWFya2Rvd24oKSkKICByZXR1cm4ocCkKfQpgYGAKCjxmb250IGNvbG9yPSJyZWQiPlVwZGF0ZSAyMDI0LTExLTIyPC9mb250PgoKQWx0ZXJuYXRpdmUgZGVzaWduLCB3aXRoIGluZGl2aWR1YWwgcG9pbnRzIGFuZCBub3QgcmVsYXRpdmUgdG8gU2NQaG80LiBBbHNvLCBpbmRpdmlkdWFsIGRhdGFwb2ludHMgd2VyZSBwbG90dGVkIGZvciBzYW1wbGVzIHdpdGggXDwxMCByZXBsaWNhdGVzCgpgYGB7cn0KbXlfcGxvdF9zdWJzZXRfeGltZXJhX2FsdCA8LSBmdW5jdGlvbihzeW1ib2xzKXsKICAjIHRoaXMgZnVuY3Rpb24gcGxvdHMgYSBzdWJzZXQgb2YgdGhlIGNoaW1lcmFzIGFzIGhvcml6b250YWwgYmFyIHBsb3RzCiAgIyBzaG93aW5nIHRoZSBSZWwuIEFfUEhPMiBhbmQgJUFfcGhvMuKIhiB2YWx1ZXMKICAjIGl0IHRha2VzIGFzIGlucHV0IGEgdmVjdG9yIGNvbnRhaW5pbmcgdGhlIHN5bWJvbHMgZm9yIHRoZSBjaGltZXJhcyBmb3IgCiAgIyBwbG90dGluZy4gdGhlIG9yZGVyIGluIHRoZSB2ZWN0b3IgZGV0ZXJtaW5lcyB0aGUgcGxvdCBvcmRlcgogICMgdGhlIGVuZG9nZW5vdXMgU2NQaG80IGFuZCBDZ1BobzQgYXJlIGltcGxpZWQKICBtaXNzaW5nIDwtIHNldGRpZmYoc3ltYm9scywgeGltZXJhJHN5bWJvbCkKICBpZihsZW5ndGgobWlzc2luZykgIT0gMCkKICAgIHN0b3AocGFzdGUobWlzc2luZywgImFyZSBub3QgZm91bmQiLCBzZXAgPSAiICIpKQogIAogIHRtcCA8LSBmaWx0ZXIoZGF0X3NlcCwgc3ltYm9sICVpbiUgYygiU1NTU1MiLCAiQ0NDQ0MiLCBzeW1ib2xzKSkgJT4lIAogICAgbXV0YXRlKGhvc3QgPSBmY3RfcmVsZXZlbChob3N0LCAiUEhPMiIpLAogICAgICAgICAgIHN5bWJvbCA9IGZhY3RvcihzeW1ib2wsIGxldmVscyA9IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVuaXF1ZShjKCJTU1NTUyIsICJDQ0NDQyIsIHN5bWJvbHMpKSkpCgogIHRtcCAlPiUgY291bnQoc3ltYm9sLCBob3N0KSAlPiUgcHJpbnQoKQogICMgbGFiZWxsZXIKICBwYXIuZXhwbGFpbiA8LSBjKAogICAgUEhPMiA9ICJBPHN1Yj5QSE8yPC9zdWI+IiwKICAgICNib29zdCA9ICJCb29zdCIsCiAgICBwaG8yID0gIkE8c3ViPnBobzLiiIY8L3N1Yj4iCiAgKQogIAogIHAgPC0gZ2dwbG90KHRtcCwgYWVzKHkgPSBzeW1ib2wsIHggPSBBKSkgKwogICAgZ2VvbV9iYXIoc3RhdCA9ICJzdW1tYXJ5IiwgZnVuID0gIm1lYW4iLCAKICAgICAgICAgICAgIHdpZHRoID0gMC41LCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZ3JheTgwIikgKwogICAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gIm1lYW5fY2xfYm9vdCIsIGdlb20gPSAibGluZXJhbmdlIiwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJzdGVlbGJsdWU0IikgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZmlsdGVyKHRtcCwgIXN5bWJvbCAlaW4lIGMoIkNDQ0NDIiwgIlNTU1NTIikpLCAKICAgICAgICAgICAgICAgc2l6ZSA9IDAuNiwgc2hhcGUgPSAzLCBjb2xvciA9ICJncmF5MzAiKSArCiAgICAjZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMSwgbGluZXR5cGUgPSAyLCBjb2xvciA9ICJncmF5MzAiKSArCiAgICAjZ2VvbV9lcnJvcmJhcihhZXMoeG1pbiA9IHJBIC0gclNFLCB4bWF4ID0gckEgKyByU0UpLCB3aWR0aCA9IDAuMikgKwogICAgZmFjZXRfd3JhcCh+aG9zdCwgc2NhbGVzID0gImZyZWVfeCIsIyBzd2l0Y2ggPSAieCIsCiAgICAgICAgICAgICAgbGFiZWxsZXIgPSBsYWJlbGxlcihob3N0ID0gcGFyLmV4cGxhaW4pKSArCiAgICBzY2FsZV95X2Rpc2NyZXRlKGxpbWl0cyA9IHJldikgKyAKICAgIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMC4wMiwgMC4wNSkpKSArCiAgICB0aGVtZV9jb3dwbG90KCkgKyBwYW5lbF9ib3JkZXIoY29sb3IgPSAiZ3JheTMwIikgKwogICAgYmFja2dyb3VuZF9ncmlkKG1ham9yID0gInkiLCBtaW5vciA9ICJub25lIikgKwogICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gImNvdXJpZXIiKSwKICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBzdHJpcC5wbGFjZW1lbnQgPSAib3V0c2lkZSIsCiAgICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfbWFya2Rvd24oKSkKICByZXR1cm4obGlzdChkYXRhID0gdG1wLCBwbG90ID0gcCkpCn0KYGBgCgojIyMgTWluaW1hbCBDZ1BobzQgcGFydHMgZm9yIEFfcGhvMgoKVGhlIGNoaW1lcmEgd2l0aCB0aGUgbGVhc3QgYW1vdW50IG9mIENnUGhvNCBhbmQgeWV0IGhhdmUgYXBwcmVjaWFibGUgYWN0aXZpdHkgaW4gdGhlIGFic2VuY2Ugb2YgUGhvMiBpcyBUaGVzZSBpbmNsdWRlIFNTU1NTLCBDQ0NDQywgU1NTU0MsIENTU1NTLCBTU0NTUywgQ1NDU1MsIENTU1NDLCBDU0NTQwoKYGBge3J9CnNlbGVjdGVkIDwtIGFzLmNoYXJhY3RlcigKICBleHByZXNzaW9uKENTU1NTLCBTQ1NTUywgQ0NTU1MsIFNTQ1NTLCBDU0NTUywgU0NDU1MsIENDQ1NTLCBTU1NTQywgU1NDU0MsIENTQ1NDLCBDU1NjQykpCiNzZWxlY3RlZCA8LSBmaWx0ZXIobWV0YSwgc3ltYm9sICVpbiUgc2VsZWN0ZWQpICU+JSBwdWxsKHBsYXNtaWQpCm15X3Bsb3Rfc3Vic2V0X3hpbWVyYShzZWxlY3RlZCkKZ2dzYXZlKCIuLi9pbWcvMjAyNDAzMDgtc2VsZWN0ZWQtY2hpbWVyYS1yZWwtYWN0aXZpdHkucG5nIiwgd2lkdGggPSA0LCBoZWlnaHQgPSA0KQojIHBsb3Qgd2l0aCBhYnNvbHV0ZSBBIG5vdCByZWxhdGl2ZSwgYW5kIHBsb3QgaW5kaXZpZHVhbCBkYXRhIHBvaW50cwpwbG90LnN1YjEgPC0gbXlfcGxvdF9zdWJzZXRfeGltZXJhX2FsdChzZWxlY3RlZCkKcHJpbnQocGxvdC5zdWIxJHBsb3QpCmdnc2F2ZSgiLi4vaW1nLzIwMjQxMTIyLXNlbGVjdGVkLWNoaW1lcmEtcmVsLWFjdGl2aXR5LnBuZyIsIHdpZHRoID0gNCwgaGVpZ2h0ID0gNCkKIyBzYXZlIHRoZSBkYXRhIGZvciBwdWJsaWNhdGlvbgpwbG90LnN1YjEkZGF0YSAlPiUgCiAgc2VsZWN0KHBsYXNtaWRfaWQgPSBwbGFzbWlkLCBjaGltZXJhX21ha2V1cCA9IHN5bWJvbCwgaG9zdCwgQSkgJT4lIAogIHdyaXRlX3RzdigiLi4vb3V0cHV0LzIwMjUwMjEzLUZpZy01Qy1kYXRhLnRzdiIpCmBgYAoKIyMjIFJlZ2lvbiAxLTMgbWFpbiBlZmZlY3RzIGFuZCBpbnRlcmFjdGlvbnMKCmBgYHtyfQojIHNlbGVjdCB0aGUgY2hpbWVyYXMKc2VsZWN0ZWQgPC0gYXMuY2hhcmFjdGVyKAogIGV4cHJlc3Npb24oU1NTU1MsIENTU1NTLCBTQ1NTUywgU1NDU1MsIENDU1NTLCBDU0NTUywgU0NDU1MsIENDQ1NTKQopCgojIGV4dHJhY3QgdGhlIGRhdGEKdG1wIDwtIHhpbWVyYSAlPiUgCiAgZmlsdGVyKHN5bWJvbCAlaW4lIHNlbGVjdGVkLCBzZXQgPT0gIk0iKSAlPiUgCiAgc2VsZWN0KHBsYXNtaWQsIHN5bWJvbCwgZ3JvdXApICU+JSAKICBpbm5lcl9qb2luKGRhdCwgYnkgPSAicGxhc21pZCIpICU+JSAKICBtdXRhdGUoIGBSL0dgID0gWUwyLkggLyBCTDEuSCApICU+JQogIGZpbHRlcihmbGFnID09ICJwYXNzIikgJT4lIAogIHNlbGVjdCgtblJGUCwgLW5HRlAsIC13ZWxsLCAtZmxhZykKCiMgcHJlcGFyZSB0aGUgZmFjdG9yIGxldmVscwpzcGxpdCA8LSBjKDEsMSwxLDIpOyBuYW1lcyhzcGxpdCkgPC0gYygiUjEiLCAiQUQiLCAiTkxTIikKCnRtcCA8LSB0bXAgJT4lIAogIHNlcGFyYXRlX3dpZGVyX3Bvc2l0aW9uKHN5bWJvbCwgc3BsaXQpICU+JSAKICBtdXRhdGUoYWNyb3NzKFIxOk5MUywgfmZhY3RvcigueCwgbGV2ZWxzID0gYygiUyIsICJDIikpKSkKCiMgdGVzdCBBX1BITzIKcHJpbnQoIlRlc3RpbmcgQV9QSE8yIikKbG0ucmVzIDwtIHRtcCAlPiUgCiAgZmlsdGVyKGhvc3QgPT0gIlBITzIiKSAlPiUgCiAgbG0oYFIvR2AgfiAoUjEqQUQqTkxTKSwgZGF0YSA9IC4pICU+JSAKICBzdW1tYXJ5KCkKIyBhZGRpbmcgYWRqdXN0ZWQgUC12YWx1ZQpsbS5yZXMkY29lZmZpY2llbnRzIDwtIGNiaW5kKAogIGNvZWYobG0ucmVzKSwKICAiUC5hZGoiID0gcC5hZGp1c3QoY29lZihsbS5yZXMpWywnUHIoPnx0fCknXSwgbWV0aG9kID0gImhvbG0iKQopCnByaW50KGxtLnJlcykKIyBzdG9yZSB0aGUgdGVzdCByZXN1bHRzIGZvciBwbG90dGluZwpyZXMuUEhPMiA8LSBjb2VmKGxtLnJlcylbLTEsXSAlPiUgYXNfdGliYmxlKHJvd25hbWVzID0gImNvbXBvbmVudCIpCgojIHRlc3QgQV9waG8yCnByaW50KCJUZXN0aW5nIEFfcGhvMuKIhiIpCmxtLnJlcyA8LSB0bXAgJT4lIAogIGZpbHRlcihob3N0ID09ICJwaG8yIikgJT4lIAogIGxtKGBSL0dgIH4gKFIxKkFEKk5MUyksIGRhdGEgPSAuKSAlPiUgCiAgc3VtbWFyeSgpCiMgYWRkaW5nIGFkanVzdGVkIFAtdmFsdWUKbG0ucmVzJGNvZWZmaWNpZW50cyA8LSBjYmluZCgKICBjb2VmKGxtLnJlcyksCiAgIlAuYWRqIiA9IHAuYWRqdXN0KGNvZWYobG0ucmVzKVssJ1ByKD58dHwpJ10sIG1ldGhvZCA9ICJob2xtIikKKQpwcmludChsbS5yZXMpCiMgc3RvcmUgdGhlIHRlc3QgcmVzdWx0cyBmb3IgcGxvdHRpbmcKcmVzLnBobzIgPC0gY29lZihsbS5yZXMpWy0xLF0gJT4lIGFzX3RpYmJsZShyb3duYW1lcyA9ICJjb21wb25lbnQiKQoKIyBjb21iaW5lIHRoZSByZXN1bHRzCnRlc3QucmVzIDwtIGJpbmRfcm93cygKICAiQV9QSE8yIiA9IHJlcy5QSE8yLCAiQV9waG8yIiA9IHJlcy5waG8yLCAuaWQgPSAicGFyYW1ldGVyIgopCgojIHNhdmUgdGhlIG91dHB1dCBpbiBhIHRleHQgZmlsZSBmb3IgcGFwZXIKd3JpdGVfdHN2KHRlc3QucmVzLCBmaWxlID0gIi4uL291dHB1dC8yMDI1MDIxMy1yZWdpb24tMS0zLWxpbmVhci1tb2RlbC10ZXN0LnR4dCIpCmBgYAoKUGxvdCB0aGUgcmVzdWx0CgpgYGB7cn0KcGFyLmV4cGxhaW4gPC0gYygKICBBX1BITzIgPSAiQTxzdWI+UEhPMjwvc3ViPiIsCiAgQV9waG8yID0gIkE8c3ViPnBobzLiiIY8L3N1Yj4iCikKCnAgPC0gdGVzdC5yZXMgJT4lIAogIHJlbmFtZShlc3RpbWF0ZSA9IEVzdGltYXRlLCBzZSA9IGBTdGQuIEVycm9yYCkgJT4lIAogIG11dGF0ZSgKICAgIGNvbXBvbmVudCA9IGdzdWIoIkMiLCAiIiwgY29tcG9uZW50KSAlPiUgZmN0X2lub3JkZXIoKSwKICAgIHBhcmFtZXRlciA9IGZhY3RvcihwYXJhbWV0ZXIsIGxldmVscyA9IGMoIkFfUEhPMiIsICJBX3BobzIiKSksCiAgICBzaWcgPSBQLmFkaiA8IDAuMDUKICApICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBjb21wb25lbnQsIHkgPSBlc3RpbWF0ZSkpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9IDEsIGNvbG9yID0gImdyYXk1MCIpICsKICBnZW9tX2NvbChhZXMoZmlsbCA9IFAuYWRqIDwgMC4wNSksIHdpZHRoID0gMC41LCBjb2xvciA9ICJibGFjayIpICsKICBnZW9tX3BvaW50cmFuZ2UoYWVzKHltaW4gPSBlc3RpbWF0ZS1zZSwgeW1heCA9IGVzdGltYXRlK3NlKSwgc2l6ZSA9IDAuMikgKwogIGZhY2V0X3dyYXAofnBhcmFtZXRlciwgc2NhbGVzID0gImZyZWVfeSIsIG5yb3cgPSAyLAogICAgICAgICAgICAgbGFiZWxsZXIgPSBsYWJlbGxlcihwYXJhbWV0ZXIgPSBwYXIuZXhwbGFpbikpICsKICBzY2FsZV94X2Rpc2NyZXRlKCkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwoTlVMTCwgCiAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygiZ3JheTkwIiwgImdyYXk1MCIpKSArCiAgdGhlbWVfY293cGxvdCgpICsgcGFuZWxfYm9yZGVyKGNvbG9yID0gImdyYXkzMCIpICsKICBiYWNrZ3JvdW5kX2dyaWQobWFqb3IgPSAieSIsIG1pbm9yID0gInkiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxLCBzaXplID0gcmVsKDEpKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBzdHJpcC5wbGFjZW1lbnQgPSAib3V0c2lkZSIsCiAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF9tYXJrZG93bihzaXplID0gcmVsKDEpKSkKcApnZ3NhdmUoIi4uL2ltZy8yMDI0MDQwNC1yZWdpb24xLTMtZXBpc3Rhc2lzLXBsb3QucG5nIiwgd2lkdGggPSAzLjUsIGhlaWdodCA9IDQuNSkKYGBgCgojIyMgUmVnaW9uIDQgc3BsaXRzCgpXZSBoYXZlIHNvIGZhciBmb2N1c2VkIG9uIHRoZSBtYWluIHNldCB3aXRoIHRoZSA1IHJlZ2lvbiBkZXNpZ24uIEluIHRoZSBzY2F0dGVyIHBsb3QgYmVsb3csIHdlIHNlZSB0aGF0IHRoZXJlIGlzIGEgc3Vic2V0IG9mIGNoaW1lcmFzIGluIGJldHdlZW4gdGhlIFAySUQ6U2MgYW5kIFAySUQ6Q2cgb25lcy4gVGhleSBhcmUgaW50ZXJlc3RpbmcgaW4gdGhhdCB0aGVpciBBX3BobzLiiIYvQV9QSE8yIHJhdGlvcyBhcmUgaW50ZXJtZWRpYXRlLgoKIVtzY2F0dGVyXSguLi9pbWcvMjAyMzEyMjAtYWxsLWNoaW1lcmEtc2NhdHRlci1jb2xvci1ieS1QMklELnBuZykKCmBgYHtyfQpzZWxlY3RlZCA8LSBhcy5jaGFyYWN0ZXIoCiAgZXhwcmVzc2lvbihDQ0NTQywgQ0NDY3NDLCBDQ0NzY0MsIENTU0NDLCBDU1NTQywgQ1NTY3NDLCBDU1NzY0MpKQojc2VsZWN0ZWQgPC0gZmlsdGVyKG1ldGEsIHN5bWJvbCAlaW4lIHNlbGVjdGVkKSAlPiUgcHVsbChwbGFzbWlkKQpwbG90LnN1YjIgPC0gbXlfcGxvdF9zdWJzZXRfeGltZXJhX2FsdChzZWxlY3RlZCkKcGxvdC5zdWIyJHBsb3QgKyBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAuMDIsIDAuMTUpKSkKZ2dzYXZlKCIuLi9pbWcvMjAyNDExMjItUDJJRC1zcGxpdC1yZWwtYWN0aXZpdHkucG5nIiwgd2lkdGggPSAzLjUsIGhlaWdodCA9IDMuNSkKcGxvdC5zdWIyJGRhdGEgJT4lIAogIHNlbGVjdChwbGFzbWlkX2lkID0gcGxhc21pZCwgY2hpbWVyYV9tYWtldXAgPSBzeW1ib2wsIGhvc3QsIEEpICU+JSAKICB3cml0ZV90c3YoIi4uL291dHB1dC8yMDI1MDIxMy1GaWctNkItZGF0YS50c3YiKQpgYGAKClN0YXRpc3RpY2FsIHRlc3RzIGZvciBncm91cCAxCgpgYGB7cn0KIyBzZWxlY3QgdGhlIGNoaW1lcmFzCnNlbGVjdGVkIDwtIGFzLmNoYXJhY3RlcigKICBleHByZXNzaW9uKENDQ0NDLCBDQ0NTQywgQ0NDY3NDLCBDQ0NzY0MpCikKCiMgZXh0cmFjdCB0aGUgZGF0YQp0bXAgPC0geGltZXJhICU+JSAKICBmaWx0ZXIoc3ltYm9sICVpbiUgc2VsZWN0ZWQpICU+JSAKICBzZWxlY3QocGxhc21pZCwgc3ltYm9sLCBncm91cCkgJT4lIAogIGlubmVyX2pvaW4oZGF0LCBieSA9ICJwbGFzbWlkIikgJT4lIAogIG11dGF0ZSggYFIvR2AgPSBZTDIuSCAvIEJMMS5IICkgJT4lCiAgZmlsdGVyKGZsYWcgPT0gInBhc3MiKSAlPiUgCiAgc2VsZWN0KC1uUkZQLCAtbkdGUCwgLXdlbGwsIC1mbGFnKSAlPiUgCiAgbXV0YXRlKHN5bWJvbCA9IGZhY3RvcihzeW1ib2wsIGxldmVscyA9ICEhc2VsZWN0ZWQpKQoKIyB0ZXN0IEFfUEhPMgpwcmludCgiVGVzdGluZyBBX1BITzIiKQpsbS5yZXMgPC0gdG1wICU+JSAKICBmaWx0ZXIoaG9zdCA9PSAiUEhPMiIpICU+JSAKICBsbShgUi9HYCB+IHN5bWJvbCwgZGF0YSA9IC4pICU+JSAKICBzdW1tYXJ5KCkKIyBhZGRpbmcgYWRqdXN0ZWQgUC12YWx1ZQpsbS5yZXMkY29lZmZpY2llbnRzIDwtIGNiaW5kKAogIGNvZWYobG0ucmVzKSwKICAiUC5hZGoiID0gcC5hZGp1c3QoY29lZihsbS5yZXMpWywnUHIoPnx0fCknXSwgbWV0aG9kID0gImhvbG0iKQopCnByaW50KGxtLnJlcykKIyBzdG9yZSB0aGUgdGVzdCByZXN1bHRzIGZvciBwbG90dGluZwojcmVzLlBITzIgPC0gY29lZihsbS5yZXMpWy0xLF0gJT4lIGFzX3RpYmJsZShyb3duYW1lcyA9ICJjb21wb25lbnQiKQoKIyB0ZXN0IEFfcGhvMgpwcmludCgiVGVzdGluZyBBX3BobzLiiIYiKQpsbS5yZXMgPC0gdG1wICU+JSAKICBmaWx0ZXIoaG9zdCA9PSAicGhvMiIpICU+JSAKICBsbShgUi9HYCB+IHN5bWJvbCwgZGF0YSA9IC4pICU+JSAKICBzdW1tYXJ5KCkKIyBhZGRpbmcgYWRqdXN0ZWQgUC12YWx1ZQpsbS5yZXMkY29lZmZpY2llbnRzIDwtIGNiaW5kKAogIGNvZWYobG0ucmVzKSwKICAiUC5hZGoiID0gcC5hZGp1c3QoY29lZihsbS5yZXMpWywnUHIoPnx0fCknXSwgbWV0aG9kID0gImhvbG0iKQopCnByaW50KGxtLnJlcykKYGBgCgpTdGF0aXN0aWNhbCB0ZXN0cyBmb3IgZ3JvdXAgMgoKYGBge3J9CiMgc2VsZWN0IHRoZSBjaGltZXJhcwpzZWxlY3RlZCA8LSBhcy5jaGFyYWN0ZXIoCiAgZXhwcmVzc2lvbiggQ1NTQ0MsIENTU1NDLCBDU1Njc0MsIENTU3NjQyApCikKCiMgZXh0cmFjdCB0aGUgZGF0YQp0bXAgPC0geGltZXJhICU+JSAKICBmaWx0ZXIoc3ltYm9sICVpbiUgc2VsZWN0ZWQpICU+JSAKICBzZWxlY3QocGxhc21pZCwgc3ltYm9sLCBncm91cCkgJT4lIAogIGlubmVyX2pvaW4oZGF0LCBieSA9ICJwbGFzbWlkIikgJT4lIAogIG11dGF0ZSggYFIvR2AgPSBZTDIuSCAvIEJMMS5IICkgJT4lCiAgZmlsdGVyKGZsYWcgPT0gInBhc3MiKSAlPiUgCiAgc2VsZWN0KC1uUkZQLCAtbkdGUCwgLXdlbGwsIC1mbGFnKSAlPiUgCiAgbXV0YXRlKHN5bWJvbCA9IGZhY3RvcihzeW1ib2wsIGxldmVscyA9ICEhc2VsZWN0ZWQpKQoKIyB0ZXN0IEFfUEhPMgpwcmludCgiVGVzdGluZyBBX1BITzIiKQpsbS5yZXMgPC0gdG1wICU+JSAKICBmaWx0ZXIoaG9zdCA9PSAiUEhPMiIpICU+JSAKICBsbShgUi9HYCB+IHN5bWJvbCwgZGF0YSA9IC4pICU+JSAKICBzdW1tYXJ5KCkKIyBhZGRpbmcgYWRqdXN0ZWQgUC12YWx1ZQpsbS5yZXMkY29lZmZpY2llbnRzIDwtIGNiaW5kKAogIGNvZWYobG0ucmVzKSwKICAiUC5hZGoiID0gcC5hZGp1c3QoY29lZihsbS5yZXMpWywnUHIoPnx0fCknXSwgbWV0aG9kID0gImhvbG0iKQopCnByaW50KGxtLnJlcykKIyBzdG9yZSB0aGUgdGVzdCByZXN1bHRzIGZvciBwbG90dGluZwojcmVzLlBITzIgPC0gY29lZihsbS5yZXMpWy0xLF0gJT4lIGFzX3RpYmJsZShyb3duYW1lcyA9ICJjb21wb25lbnQiKQoKIyB0ZXN0IEFfcGhvMgpwcmludCgiVGVzdGluZyBBX3BobzLiiIYiKQpsbS5yZXMgPC0gdG1wICU+JSAKICBmaWx0ZXIoaG9zdCA9PSAicGhvMiIpICU+JSAKICBsbShgUi9HYCB+IHN5bWJvbCwgZGF0YSA9IC4pICU+JSAKICBzdW1tYXJ5KCkKIyBhZGRpbmcgYWRqdXN0ZWQgUC12YWx1ZQpsbS5yZXMkY29lZmZpY2llbnRzIDwtIGNiaW5kKAogIGNvZWYobG0ucmVzKSwKICAiUC5hZGoiID0gcC5hZGp1c3QoY29lZihsbS5yZXMpWywnUHIoPnx0fCknXSwgbWV0aG9kID0gImhvbG0iKQopCnByaW50KGxtLnJlcykKYGBgCgojIyBSZWdpb24gbWFpbiBlZmZlY3QKCmBgYHtyfQpzcGxpdCA8LSBjKDEsMSwxLDEsMSk7IG5hbWVzKHNwbGl0KSA8LSBwYXN0ZTAoIlAiLCAxOjUpCnRtcCA8LSB4aW1lcmEgJT4lIAogIGZpbHRlcihzZXQgPT0gIk0iLCBncm91cCAhPSAibi5mLiIpICU+JSAKICBzZXBhcmF0ZV93aWRlcl9wb3NpdGlvbihzeW1ib2wsIHNwbGl0KSAlPiUgCiAgbXV0YXRlKGFjcm9zcyhQMTpQNSwgfmZhY3RvcigueCwgbGV2ZWxzID0gYygiUyIsICJDIikpKSkKbG0gPC0gbG0oQV9waG8yIH4gKFAxK1AyK1AzK1A0K1A1KSwgZGF0YSA9IHRtcCkKc3VtbWFyeShsbSkKYGBgCgpUaGUgbWFpbiBlZmZlY3RzIHdlcmUgY2FsY3VsYXRlZCBieSBhdmVyYWdpbmcgb3ZlciBhbGwgY2hpbWVyYXMgd2l0aCBDZ1BobzQgcmVnaW9uIGF0IHRoZSByZXNwZWN0aXZlIHBvc2l0aW9uLiBJJ2QgbGlrZSB0byBicmVhayB0aGVtIGRvd24gYnkgYmFja2dyb3VuZHMuIEZvciBleGFtcGxlLCBmb3IgcmVnaW9uIDMsIEknZCBsaWtlIHRvIHNlZSB0aGUgcGFpcndpc2UgY29tcGFyaXNvbnMgYmV0d2VlbiBDQ0NTUyBhbmQgQ0NTU1MsIHdoZXJlIG9ubHkgcmVnaW9uIDMgZGlmZmVycy4gVGhlIHN0ZXBzIGFyZQoKMS4gIHNlbGVjdCB0aGUgcmVnaW9uIHRvIGJlIGNvbXBhcmVkLiBzcGxpdCB0aGUgc3ltYm9sIGludG8gdHdvIHBhcnRzIC0gdGhlIGdlbm90eXBlIG9mIHRoZSBmb2NhbCByZWdpb24gYW5kIHRoZSByZXN0CjIuICBncm91cCBieSB0aGUgc2Vjb25kIHBhcnQgKHJlc3QpIGFuZCBjYWxjdWxhdGUgdGhlIGRpZmZlcmVudGlhbAoKYGBge3J9Cm15X2NhbGNfcmVnaW9uX2VmZmVjdCA8LSBmdW5jdGlvbihyZWdpb24sIHZhcmlhYmxlKXsKICAjIHRoaXMgZnVuY3Rpb24gdGFrZXMgdGhlIG5hbWUgb2YgYSB2YXJpYWJsZSBvZiBpbnRlcmVzdAogICMgeCBzcGVjaWZpZXMgdGhlIGZvcmVncm91bmQgcmVnaW9uLCB3aGljaCB3aWxsIGJlIGV4YW1pbmVkIGZvciBpdHMgZWZmZWN0IG9uCiAgIyB0aGUgdmFyaWFibGUgb2YgaW50ZXJlc3QuCiAgIyBpdCB0aGVuIHRyYW5zZm9ybXMgdGhlIHhpbWVyYSBkYXRhIGZyYW1lIHRvIHByZXNlcnZlIG9ubHkgdGhlIHZhcmlhYmxlIG9mCiAgIyBpbnRlcmVzdCwgcGl2b3RzIGl0IHdpZGVyIGFmdGVyIGdyb3VwaW5nIGJ5IHRoZSBiYWNrZ3JvdW5kIGNvbXBvc2l0aW9uLgogIAogICMgcHJlcGFyZSB0aGUgZGF0YSBieSBtdXRhdGluZyB0aGUgc3ltYm9sIGNvbHVtbiBpbnRvIGZnIGFuZCBiZwogIHZhbGlkLnZhciA8LSBjKCJBX1BITzIiLCAiQV9waG8yIiwgInJBX1BITzIiLCAickFfcGhvMiIsICJib29zdCIpCiAgaWYoIXZhcmlhYmxlICVpbiUgdmFsaWQudmFyKQogICAgc3RvcChwYXN0ZTAoIlBsZWFzZSBzcGVjaWZ5IG9uZSBvZiB0aGUgdmFsaWQgdmFyaWFibGUgbmFtZXM6IiwgCiAgICAgICAgICAgICAgICBwYXN0ZSh2YWxpZC52YXIsIGNvbGxhcHNlID0gIiwgIikpKQogIHRtcCA8LSB4aW1lcmEgJT4lIAogICAgZmlsdGVyKHNldCA9PSAiTSIpICU+JSAKICAgIHNlbGVjdChwbGFzbWlkLCBzeW1ib2wsIHZhciA9IHt7IHZhcmlhYmxlIH19KSAlPiUgCiAgICBtdXRhdGUoZmcgPSBzdHJfc3ViKHN5bWJvbCwgcmVnaW9uLCByZWdpb24pICU+JSB0b3VwcGVyKCksCiAgICAgICAgICAgYmcgPSBzeW1ib2wgJT4lIHRvdXBwZXIoKSkKICAjIHJlcGxhY2UgdGhlIGZvcmVncm91bmQgcmVnaW9uIHdpdGggWCBmb3IgZ3JvdXBpbmcKICBzdHJfc3ViKHRtcCRiZywgcmVnaW9uLCByZWdpb24pIDwtICJYIgogICMgcmVvcmdhbml6ZSB0aGUgdGliYmxlIGZvciBlYXNpZXIgaGFuZGxpbmcsIG9wdGlvbmFsCiAgdG1wIDwtIHJlbG9jYXRlKHRtcCwgZmcsIGJnLCAuYmVmb3JlID0gc3ltYm9sKSAlPiUgc2VsZWN0KC1zeW1ib2wpCiAgIyBwaXZvdCB0aGUgZGF0YSBpbnRvIGEgd2lkZSBmb3JtYXQgc3VjaCB0aGF0IGZvciBlYWNoIGJhY2tncm91bmQsIHRoZXJlCiAgIyBhcmUgdHdvIHZhbHVlcyBmb3IgdGhlIHZhcmlhYmxlIG9mIGludGVyZXN0LCBvbmUgZnJvbSB0aGUgY2hpbWVyYSB3aXRoIAogICMgQ2dQaG80J3MgdmVyc2lvbiBpbiB0aGUgZm9yZWdyb3VuZCBhbmQgYW5vdGhlciB3aXRoIFNjUGhvNCdzIHZlcnNpb24KICB0bXAgPC0gdG1wICU+JSAKICAgIHNlbGVjdChwbGFzbWlkLCBmZywgYmcsIHZhcikgJT4lIAogICAgcGl2b3Rfd2lkZXIoaWRfY29scyA9IGJnLCBuYW1lc19mcm9tID0gImZnIiwgCiAgICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IGMocGxhc21pZCwgdmFyKSkgJT4lIAogICAgdW5pdGUocGxhc21pZCwgc3RhcnRzX3dpdGgoInBsYXNtaWQiKSkgJT4lCiAgICBtdXRhdGUobGFiZWwgPSBwYXN0ZShiZywgcGxhc21pZCwgc2VwID0gIlxuIikpCiAgcmV0dXJuKHRtcCkKfQpgYGAKCmBgYCAgICAgICAgIAp4ID0gNQpwMSA8LSBteV9wbG90X3JlZ2lvbl9lZmZlY3Rfb25ldmFyKHgsICJBX1BITzIiKQpwMiA8LSBteV9wbG90X3JlZ2lvbl9lZmZlY3Rfb25ldmFyKHgsICJBX3BobzIiKQpzdWJwbG90KHAxLCBwMiwgbWFyZ2luID0gMC4wNSkgJT4lIAogIGxheW91dCh0aXRsZSA9IHBhc3RlKCJSZWdpb24iLCB4LCAic3dhcCBlZmZlY3Qgb24gQV9QSE8yIGFuZCBBX3BobzIiLCBzZXAgPSAiICIpLAogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSBwYXN0ZTAoIlJlZ2lvbiAiLCB4LCAiIGZyb20gQ2dQaG80IikpLAogICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSBwYXN0ZTAoIlJlZ2lvbiAiLCB4LCAiIGZyb20gU2NQaG80IikpICkKYGBgCgpIZXJlLCBJJ2QgbGlrZSB0byB0YWtlIHdoYXQgSSBidWlsZCBhYm92ZSBhbmQgY3JlYXRlIGEgbmV3IHRpYmJsZSwgaW4gd2hpY2ggZWFjaCByb3cgaXMgYSBkaWZmZXJlbnQgYmFja2dyb3VuZCAobWFrZXVwIG9mIHRoZSBjaGltZXJhIGV4Y2VwdCBmb3IgdGhlIGZvY2FsIHJlZ2lvbikuIFRoZSB2YWx1ZSBjb2x1bW5zIGFyZToKCjEuICBkQV9QSE8yID0gQV9QSE8yX0NnIC0gQV9QSE8yX1NjCjIuICBkQV9waG8yID0gQV9waG8yX0NnIC0gQV9waG8yX1NjCjMuICBBX1BITzJfU2MgPSBBX1BITzJfU2MKClRoZSBnb2FsIGlzIHRvIHBsb3QgZEFfUEhPMiBhbmQgZEFfcGhvMiBzaWRlLWJ5LXNpZGUgZm9yIGVhY2ggYmFja2dyb3VuZC4KCmBgYHtyfQpteV9jb21wX3JlZ2lvbl9lZmZlY3QgPC0gZnVuY3Rpb24ocmVnaW9uKXsKICAjIHRoaXMgZnVuY3Rpb24gdXNlcyBteV9jYWxjX3JlZ2lvbl9lZmZlY3QgdG8gZ2V0IHRoZSB2YWx1ZSBmb3IgdGhlIHZhcmlhYmxlIG9mIGludGVyZXN0CiAgIyB3aXRoIGVpdGhlciBDZyBvciBTYyB2ZXJzaW9uIGluIHRoZSBmb2NhbCByZWdpb24sIHNlcGFyYXRlbHkgZm9yIGVhY2ggYmFja2dyb3VuZCBjb21wb3NpdGlvbgogICMgaXQgZG9lcyBzbyBmb3IgdHdvIHZhcmlhYmxlcywgQV9QSE8yIGFuZCBBX3BobzIsIHRoZW4gY2FsY3VsYXRlIGRBX1BITzIsIGRBX3BobzIsIGFuZAogICMgY29tYmluZSB0aGVtCiAgUEhPMiA9IG15X2NhbGNfcmVnaW9uX2VmZmVjdChyZWdpb24sICJBX1BITzIiKSAlPiUgCiAgICBtdXRhdGUoZEFfUEhPMiA9IHZhcl9DIC0gdmFyX1MsCiAgICAgICAgICAgIyBtZWFuIEFfUEhPMgogICAgICAgICAgIE1fUEhPMiA9ICh2YXJfUyArIHZhcl9DKS8yLAogICAgICAgICAgIE5GID0gaWZlbHNlKE1fUEhPMiA8PTMuNSwgVFJVRSwgRkFMU0UpKSAlPiUgCiAgICBzZWxlY3QoLXZhcl9TLCAtdmFyX0MpCiAgCiAgcGhvMiA9IG15X2NhbGNfcmVnaW9uX2VmZmVjdChyZWdpb24sICJBX3BobzIiKSAlPiUgCiAgICBtdXRhdGUoZEFfcGhvMiA9IHZhcl9DIC0gdmFyX1MsIAogICAgICAgICAgIE1fcGhvMiA9ICh2YXJfUyArIHZhcl9DKS8yKSAlPiUgCiAgICBzZWxlY3QoLXZhcl9TLCAtdmFyX0MpCiAgCiAgZGF0IDwtIGZ1bGxfam9pbihQSE8yLCBwaG8yLCBieSA9IGMoImJnIiwgInBsYXNtaWQiLCAibGFiZWwiKSkgJT4lIAogICAgc2VsZWN0KGJnLCBwbGFzbWlkLCBkQV9QSE8yLCBkQV9waG8yLCBNX1BITzIsIE1fcGhvMiwgTkYpCiAgCiAgcmV0dXJuKGRhdCkKfQoKCmBgYAoKYGBge3J9Cm15X3Bsb3RfcmVnaW9uX2VmZmVjdF90d292YXJfbGluZSgiMSIsICI0IikjICU+JSBnZ3Bsb3RseSgpCmdnc2F2ZSgiLi4vaW1nLzIwMjQwMzEwLXJlZ2lvbi1zd2FwLWVmZmVjdC0xLW9uLTQucG5nIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA0KQpteV9wbG90X3JlZ2lvbl9lZmZlY3RfdHdvdmFyX2xpbmUoIjMiLCAiNCIpIyAlPiUgZ2dwbG90bHkoKQpnZ3NhdmUoIi4uL2ltZy8yMDI0MDMxMC1yZWdpb24tc3dhcC1lZmZlY3QtMy1vbi00LnBuZyIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNCkKCmBgYAoKYGBgICAgICAgICAgCm15X3Bsb3RfcmVnaW9uX2VmZmVjdF90d292YXJfbGluZSgiNCIsICI1IikjICU+JSBnZ3Bsb3RseSgpCmdnc2F2ZSgiLi4vaW1nLzIwMjMxMjIxLXJlZ2lvbi1zd2FwLWVmZmVjdC00LW9uLTUucG5nIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA0LCBkcGkgPSAxNTApCm15X3Bsb3RfcmVnaW9uX2VmZmVjdF90d292YXJfbGluZSgiNSIsICI0IikjICU+JSBnZ3Bsb3RseSgpCmdnc2F2ZSgiLi4vaW1nLzIwMjMxMjI0LXJlZ2lvbi1zd2FwLWVmZmVjdC01LW9uLTQucG5nIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA0LCBkcGkgPSAyMDApCmBgYAoKVGhlIG1haW4gcGxvdHRpbmcgZnVuY3Rpb25zIGFyZSBub3cgaW4gYSBzZXBhcmF0ZSBzY3JpcHQgZmlsZSBpbiBgLi4vc2NyaXB0YC4gVGhlIHBsb3R0aW5nIGZ1bmN0aW9uIGJlbG93IGlzIHRvIGFkYXB0IHRoZSBwbG90IGZvciBhIGZpZ3VyZSBpbiB0aGUgcGFwZXIsIHNpbXVsdGFuZW91c2x5IHNob3dpbmcgcmVnaW9ucyAxLTMuCgpgYGB7cn0KbXlfcGxvdF9yZWdpb25fZWZmZWN0X3R3b3Zhcl9saW5lX3BhciA8LSBmdW5jdGlvbihyZWdpb25zKXsKICAjIHRoaXMgZnVuY3Rpb24gdXNlcyBteV9jb21wX3JlZ2lvbl9lZmZlY3QgdG8gZ2VuZXJhdGUgdGhlIGRhdGEKICAjIGFuZCBwbG90IHRoZSBkaWZmZXJlbmNlIGluIEFfUEhPMiBhbmQgQV9waG8yIGJldHdlZW4gdGhlIENnUGhvNCB2cyBTY1BobzQKICAjIGluIHRoZSBmb2NhbCByZWdpb24KICBkYXQgPC0gbWFwX2RmcihyZWdpb25zLCBcKHJlZ2lvbikgbXlfY29tcF9yZWdpb25fZWZmZWN0KHJlZ2lvbiksIC5pZCA9ICJyZWdpb24iKSAlPiUgCiAgICBwaXZvdF9sb25nZXIoY29scyA9IGMoZEFfUEhPMiwgZEFfcGhvMiksIAogICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gImhvc3QiLCB2YWx1ZXNfdG8gPSAiZGlmZiIpICU+JSAKICAgIG11dGF0ZShob3N0ID0gZmN0X3JlY29kZShob3N0LCBgUEhPMmAgPSAiZEFfUEhPMiIsIGBwaG8y4oiGYCA9ICJkQV9waG8yIiksCiAgICAgICAgICAgaG9zdCA9IGZjdF9yZWxldmVsKGhvc3QsICJQSE8yIikpCiAgIyBzcGVjaWZ5IGdyb3VwaW5nIHZhcmlhYmxlCiAgZGF0IDwtIG11dGF0ZShkYXQsIAogICAgICAgICAgICAgICAgZ3JwID0gc3RyX3N1YihiZywgNCwgNCkgJT4lIHRvdXBwZXIoKSwKICAgICAgICAgICAgICAgIGdycCA9IGZjdF9yZWNvZGUoZ3JwLCBDZ1BobzQgPSAiQyIsIFNjUGhvNCA9ICJTIikpIywKICAgICAgICAgICAgICAgICNzaCA9IHN0cl9zdWIoYmcsIDUsIDUpICU+JSB0b3VwcGVyKCksCiAgICAgICAgICAgICAgICAjc2ggPSBmY3RfcmVjb2RlKHNoLCBDZ1BobzQgPSAiQyIsIFNjUGhvNCA9ICJTIikgKQogICMgc3BlY2lmeSBhcnJvdyBhbm5vdGF0aW9uCiAgYXJyb3cueCA9IDAuNwogIGFycm93LnkgPSAobWF4KGRhdCRkaWZmKSAtIG1pbihkYXQkZGlmZikpIC8gNSAKICAjIHBsb3QKICBwIDwtIGRhdCAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSBob3N0LCB5ID0gZGlmZiwgbGFiZWwgPSBiZykpICsKICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZ3JwKSwgc2l6ZSA9IDIsIGFscGhhID0gMC44LAogICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcigwLjEpKSArIAogICAgZ2VvbV9saW5lKGFlcyhncm91cCA9IGJnKSwgbGluZXdpZHRoID0gMC4yLCBhbHBoYSA9IDAuOCkgKwogICAgZmFjZXRfZ3JpZChyZWdpb24gfiBncnAsIGxhYmVsbGVyID0gbGFiZWxsZXIoCiAgICAgIGdycCA9IGMoQ2dQaG80ID0gIlAySUQ6Q2ciLCBTY1BobzQgPSAiUDJJRDpTYyIpLAogICAgICByZWdpb24gPSBsYWJlbF9ib3RoCiAgICApKSArCiAgICBzY2FsZV9jb2xvcl9tYW51YWwoIlAySUQ6IiwgdmFsdWVzID0gYygib3JhbmdlIiwgImdyYXkzMCIpLCBndWlkZSA9ICJub25lIikgKwogICAgI3NjYWxlX3NoYXBlX21hbnVhbCgiREJEOiIsIHZhbHVlcyA9IGMoMTksIDEpKSArCiAgICB5bGFiKCJSZWdpb24gc3dhcCBlZmZlY3QgKENnLVNjKSIpICsKICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE4KSArIAogICAgdGhlbWUoCiAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC45KSksCiAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAzKSwKICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjgpKSwKICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjgpKSwKICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC45KSksCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpCiAgICApCiAgcmV0dXJuKHApCn0KYGBgCgpgYGB7cn0KbXlfcGxvdF9yZWdpb25fZWZmZWN0X3R3b3Zhcl9saW5lX3BhcihjKDEsMykpCmdnc2F2ZSgiLi4vaW1nLzIwMjQwMzEwLXJlZ2lvbi1zd2FwLWVmZmVjdC0xbjMtb24tNC5wbmciLAogICAgICAgd2lkdGggPSA1LCBoZWlnaHQgPSAzLjUpCmBgYAoKYGBge3J9Cm15X3Bsb3RfcmVnaW9uX2VmZmVjdF90d292YXJfc2lkZSA8LSBmdW5jdGlvbihyZWdpb24pewogICMgdGhpcyBmdW5jdGlvbiB1c2VzIG15X2NvbXBfcmVnaW9uX2VmZmVjdCB0byBnZW5lcmF0ZSB0aGUgZGF0YQogICMgYW5kIHBsb3QgdGhlIGRpZmZlcmVuY2UgaW4gQV9QSE8yIGFuZCBBX3BobzIgYmV0d2VlbiB0aGUgQ2dQaG80IHZzIFNjUGhvNAogICMgaW4gdGhlIGZvY2FsIHJlZ2lvbgogIGRhdCA8LSBteV9jb21wX3JlZ2lvbl9lZmZlY3QocmVnaW9uKSAlPiUgCiAgICBwaXZvdF9sb25nZXIoY29scyA9IGMoZEFfUEhPMiwgZEFfcGhvMiksIAogICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gImhvc3QiLCB2YWx1ZXNfdG8gPSAiZGlmZiIpICU+JSAKICAgIG11dGF0ZShob3N0ID0gZmN0X3JlY29kZShob3N0LCBgUEhPMmAgPSAiZEFfUEhPMiIsIGBwaG8yYCA9ICJkQV9waG8yIiksCiAgICAgICAgICAgaG9zdCA9IGZjdF9yZWxldmVsKGhvc3QsICJQSE8yIikpCiAgcCA8LSBkYXQgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gYmcsIHkgPSBkaWZmLCBncm91cCA9IGhvc3QpKSArCiAgICBnZW9tX2NvbChhZXMoZmlsbCA9IGhvc3QpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSkpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGhvc3QuY29sb3JzKSArCiAgICB5bGFiKCJSZWdpb24gc3dhcCBkaWZmIChDZyB2cyBTYykiKSArCiAgICB0aGVtZV9jb3dwbG90KGZvbnRfc2l6ZSA9IDIwKSArIAogICAgcGFuZWxfYm9yZGVyKGNvbG9yID0gImdyYXkzMCIpICsKICAgIGJhY2tncm91bmRfZ3JpZChtYWpvciA9ICJ5IiwgbWlub3IgPSAibm9uZSIpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgZmFtaWx5ID0gImNvdXJpZXIiKSwKICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQogIHJldHVybihwKQp9CmBgYAoKIyMgUDJJRDpDZ19EQkQ6U2MgZmFpbAoKSGlnaGxpZ2h0IHRoZSBzdWJzZXQgb2YgdGhlIGNoaW1lcmFzIHdpdGggUDJJRDpDZyArIERCRDpTYywgbW9zdCBvZiB3aGljaCBhcmUgbm9uIGZ1bmN0aW9uYWwKCmBgYHtyfQpteV9zY2F0dGVyX3Bsb3QoIlhYWFhDUyIpICsKICAgIGxhYnMoeCA9IGJxdW90ZShBW1BITzJdKSwgeSA9IGJxdW90ZShBW3BobzJdKSkKZ2dzYXZlKGZpbGVuYW1lID0gIi4uL2ltZy8yMDI0MDIxNC1QaG80LWNoaW1lcmFzLXNjYXR0ZXItUDJJRF9DZy1EQkRfU2MucG5nIiwKICAgICAgIHBsb3QgPSBwLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQsIGRwaSA9IDMwMCkKYGBgCgpgYGB7cn0KeCA8LSBteV9kYXRhX3NlbGVjdChwYXR0ZXJuID0gIlhYWFhDUyIsIFNldCA9ICJNIikKbXlfZGF0YV9wcmVwKHgpICU+JSAKICBtdXRhdGUoZ3JvdXAgPSBmY3RfcmVjb2RlKGdyb3VwLCAiY2hpbWVyYSIgPSAibi5mLiIpKSAlPiUgCiAgbXlfcGxvdF9jb21wb25lbnRzKCkKZ2dzYXZlKCIuLi9pbWcvMjAyNDAyMTMtUDJJRF9DZy1EQkRfU2MtY29tcG9uZW50cy5wbmciLCB3aWR0aCA9IDgsIGhlaWdodCA9IDUpCmBgYAoKIyMgVHJpYW5nbGUgaGVhdG1hcAoKRmlyc3QsIHdyaXRlIGEgZnVuY3Rpb24gdG8gZ2VuZXJhdGUgdGhlIGRhdGEgZm9yIHBsb3R0aW5nLiBJZiB3ZSBhcmUgZ29pbmcgdG8gdXNlIGdncGxvdCwgd2UgbmVlZCBhIHRpYmJsZSB0byBzdG9yZSB0aGUgZGF0YSwgc29tZXRoaW5nIGluIHRoZSBmb2xsb3dpbmcgZm9ybQoKfCBwbGFzbWlkIHwgc3ltYm9sIHwgUmVnaW9uQSB8IFJlZ2lvbkIgfCBBX1BITzIgfCBBX3BobzIgfCByQV9QSE8yIHwgYm9vc3QgfCBwZXJjX3BobzIgfAp8Oi0tLS0tLS0tfDotLS0tLS0tfDotLS0tLS0tLXw6LS0tLS0tLS18Oi0tLS0tLS18Oi0tLS0tLS18Oi0tLS0tLS0tfDotLS0tLS18Oi0tLS0tLS0tLS18CnwgMjA5ICAgICB8IENDU0NDICB8IDMgICAgICAgfCAzICAgICAgIHwgOC4yNSAgIHwgNy44MiAgIHwgMC40NjggICB8IDEuMDYgIHwgMC45NCAgICAgIHwKCklmIHdlIGFyZSBvayB3aXRoIHVzaW5nIG5vbiBnZ3Bsb3QgLSBoZWF0bWFwcyBhcmUgbm90IGdncGxvdCdzIHN0cmVuZ3RoIGFueXdheXMgLSB3ZSBjYW4ganVzdCBidWlsZCBhIG1hdHJpeC4KCk5vdGUgdGhhdCB0aGlzIHdheSBvZiBzdW1tYXJpemluZyB0aGUgZGF0YSBoYXMgbWFueSBsaW1pdGFpdG9uczogMSkgaXQgcmVxdWlyZXMgc3BlY2lmeWluZyB0aGUgcmVmZXJlbmNlLCBlaXRoZXIgQ0NDQ0Mgb3IgU1NTU1MuIEV2ZXJ5dGhpbmcgaXMgbWVhc3VyZWQgYWdhaW5zdCB0aGF0OyAyKSBpdCBvbmx5IHNob3dzIHBhaXJ3aXNlICh0d28gcmVnaW9uKSBpbnRlcmFjdGlvbnMuIFRoaXMgdHVybnMgb3V0IHRvIGJlIGZpbmUgd2l0aCBmaXZlIHJlZ2lvbnMsIHNpbmNlIGV2ZXJ5IGNoaW1lcmEgY2FuIGJlIGV4cHJlc3NlZCBhcyBlaXRoZXIgYSAwLCAxIG9yIDIgcmVnaW9uIHN3YXAgZnJvbSBvbmUgb2YgdGhlIHR3byByZWZlcmVuY2UgZ2Vub3R5cGVzLiBXaXRoIDYgb3IgbW9yZSByZWdpb25zLCBoaWdoZXIgbGV2ZWwgKDMgb3IgbW9yZSByZWdpb24pIGludGVyYWN0aW9ucyBjYW5ub3QgYmUgdmlzdWFsaXplZCB0aGlzIHdheS4gQmVjYXVzZSBvZiB0aGlzLCB3ZSB3aWxsIGZvY3VzIG9uIGp1c3QgdGhlIG1haW4gc2V0IGZvciB0aGlzIGFuYWx5c2lzLgoKVG8gYnVpbGQgdGhlIG1hdHJpeCwgd2UgbmVlZCB0byBmaXJzdCBpZGVudGlmeSB0aGUgY2hpbWVyYXMgdGhhdCBiZWxvbmcgdG8gdGhlIHNldC4gRm9yIHRoYXQsIHdlIHdpbGwgdXNlIHRoZSAibWFpbiIgc2V0LCB3aXRoIHRoZSBmaXZlIHJlZ2lvbiBzcGxpdCwgZm9yIHRoZSBtb21lbnQgYXQgbGVhc3QuIFRoZSBmdW5jdGlvbiB3aWxsIGZpcnN0IGRldGVybWluZSB3aGljaCByZWZlcmVuY2UgdG8gdXNlLiBJZiB3ZSB1c2UgU1NTU1MgYXMgdGhlIHJlZmVyZW5jZSwgZm9yIGV4YW1wbGUsIHdlIHdpbGwgYXNzaWduIDAgdG8gdGhlIHJlZmVyZW5jZS4gQWxsIG90aGVyIGNoaW1lcmFzIHdpdGggMSBvciAyIHJlZ2lvbnMgZnJvbSBDZyB3aWxsIGJlIHVzZWQgdG8gZmlsbCBhbiB1cHBlciB0cmlhbmd1bGFyIG1hdHJpeCwgdXNpbmcgb25lIG9mIHRoZSB2YWx1ZXMgb2YgaW50ZXJlc3QsIGUuZy4sIEFfUEhPMi4KCmBgYHtyfQpteV91cHBlcl90cmlhbmd1bGFyX21hdCA8LSBmdW5jdGlvbihhbHQgPSAiQyIsIHZhciA9ICJBX1BITzIiLCBuZi5hcy5uYSA9IEYpewogICMgZ2l2ZW4gdGhlIGFsdGVybmF0aXZlIGFsbGVsZSAoQy9TKSBhbmQgYSB2YXJpYWJsZSBvZiBpbnRlcmVzdCwgZS5nLiwgQV9QSE8yLAogICMgb3V0cHV0IGFuIHVwcGVyIHRyaWFuZ3VsYXIgbWF0cml4IGNvbnRhaW5pbmcgdGhlIHZhbHVlcyBmcm9tIHRoZSB2YXJpYWJsZSAKICAjIG9mIGludGVyZXN0LCB3aXRoIHRoZSByb3cgYW5kIGNvbCBudW1iZXJzIGJhc2VkIG9uIHRoZSBmaXJzdCBhbmQgc2Vjb25kCiAgIyBwb3NpdGlvbnMgY29udGFpbmluZyB0aGUgYWx0ZXJuYXRpdmUgYWxsZWxlLiBJZiBhbGwgcG9zaXRpb25zIGNvbnRhaW4gdGhlIAogICMgcmVmZXJlbmNlIGFsbGVsZSwgdGhlIHZhbHVlIGlzIHN1YnRyYWN0ZWQgZnJvbSBhbGwgdmFsdWVzIGluIHRoZSBtYXRyaXgKICAjIHdoZW4ganVzdCBvbmUgcG9zaXRpb24gaXMgdGhlIGFsdGVybmF0aXZlIGFsbGVsZSwgdGhlIHZhbHVlIGluIHRoZSBkaWFnb25hbAogICMgaXMgc2V0LiB3aGVuIHRoZXJlIGFyZSBtb3JlIHRoYW4gMiByZWdpb25zIGNvbnRhaW5pbmcgdGhlIGFsdGVybmF0aXZlIGFsbGVsZQogICMgc2tpcC4KICAjIGlmICJuZi5hcy5uYSA9IFRSVUUiLCBldmFsdWF0ZSBpZiB0aGUgYWN0aXZpdHkgb2YgZWl0aGVyIG9mIHRoZSB0d28gY2hpbWVyYXMKICAjIGJlaW5nIGNvbXBhcmVkIGlzIG5vbiBmdW5jdGlvbmFsLiBpZiB5ZXMsIHNldCB0aGUgY29ycmVzcG9uZGluZyBtYXRyaXggdmFsdWUKICAjIHRvIE5BCiAgb3V0X21hdCA8LSBtYXRyaXgoTkEsIG5yb3cgPSA1LCBuY29sID0gNSkKICByZWZfdmFsIDwtIE5BCiAgZGF0IDwtIGZpbHRlcih4aW1lcmEsIHNldCA9PSAiTSIpICU+JSAKICAgIG11dGF0ZShTID0gYXMuY2hhcmFjdGVyKHN5bWJvbCkgJT4lIHRvdXBwZXIoKSkKICBpZihuZi5hcy5uYSl7CiAgICBkYXQgPC0gZmlsdGVyKGRhdCwgZ3JvdXAgIT0gIm4uZi4iKQogIH0KICBmb3IoaSBpbiBzZXEoMSwgbnJvdyhkYXQpKSl7CiAgICBzeW1ib2wgPSBkYXRbaSwgIlMiXQogICAgIyBkZXRlcm1pbmUgd2hpY2ggcG9zaXRpb25zIGNvbnRhaW4gdGhlIGFsdGVybmF0aXZlIGFsbGVsZQogICAgcCA9IHN0cl9sb2NhdGVfYWxsKHN5bWJvbCwgYWx0KVtbMV1dWywic3RhcnQiXQogICAgbCA9IGxlbmd0aChwKSAgICMgaG93IG1hbnkgcG9zaXRpb25zIGNvbnRhaW4gdGhlIGFsdCBhbGxlbGUKICAgIHYgPSBkYXRbW3Zhcl1dW2ldICMgcmV0cmlldmUgdGhlIHZhbHVlIG9mIHRoZSB2YXJpYWJsZQogICAgaWYobCA9PSAwKQogICAgICByZWZfdmFsID0gdgogICAgZWxzZSBpZihsID09IDEpCiAgICAgIG91dF9tYXRbcCwgcF0gPSB2CiAgICBlbHNlIGlmKGwgPT0gMikKICAgICAgb3V0X21hdFtwWzFdLCBwWzJdXSA9IHYKICB9CiAgb3V0X21hdCA9IG91dF9tYXQgLSByZWZfdmFsCiAgcmV0dXJuKG91dF9tYXQpCn0KYGBgCgpgYGB7cn0KbXlfY29tYmluZWRfdHJpYW5ndWxhcl9tYXQgPC0gZnVuY3Rpb24oYWx0ID0gIkMiKXsKICAjIGdpdmVuIHRoZSBhbHRlcm5hdGl2ZSBhbGxlbGUgKEMvUyksIG91dHB1dCBhIG1hdHJpeCBjb250YWluaW5nIHRoZSB2YWx1ZXMKICAjIGZvciBib3RoIHdpdGggYW5kIHdpdGhvdXQgUGhvMiwgYXJyYW5nZWQgaW4gdHdvIGNvbXBsZW1lbnRhcnkgdHJpYWd1bGFyCiAgIyBtYXRyaWNlcywgd2l0aCB0aGUgcm93IGFuZCBjb2wgbnVtYmVycyBiYXNlZCBvbiB0aGUgZmlyc3QgYW5kIHNlY29uZAogICMgcG9zaXRpb25zIGNvbnRhaW5pbmcgdGhlIGFsdGVybmF0aXZlIGFsbGVsZS4gSWYgYWxsIHBvc2l0aW9ucyBjb250YWluIHRoZSAKICAjIHJlZmVyZW5jZSBhbGxlbGUsIHRoZSB2YWx1ZSBpcyBzdWJ0cmFjdGVkIGZyb20gYWxsIHZhbHVlcyBpbiB0aGUgbWF0cml4CiAgIyB3aGVuIGp1c3Qgb25lIHBvc2l0aW9uIGlzIHRoZSBhbHRlcm5hdGl2ZSBhbGxlbGUsIHRoZSB2YWx1ZSBpbiB0aGUgZGlhZ29uYWwKICAjIGlzIHNldC4gd2hlbiB0aGVyZSBhcmUgbW9yZSB0aGFuIDIgcmVnaW9ucyBjb250YWluaW5nIHRoZSBhbHRlcm5hdGl2ZSBhbGxlbGUKICAjIHNraXAuCiAgb3V0X21hdCA8LSBtYXRyaXgoTkEsIG5yb3cgPSA2LCBuY29sID0gNikKICB1cHBlciA8LSBjYmluZChOQSwgbXlfdXBwZXJfdHJpYW5ndWxhcl9tYXQoYWx0LCB2YXIgPSAiQV9QSE8yIiwgKSkgJT4lIAogICAgcmJpbmQoLiwgTkEpCiAgbG93ZXIgPC0gcmJpbmQoTkEsIHQobXlfdXBwZXJfdHJpYW5ndWxhcl9tYXQoYWx0LCB2YXIgPSAiQV9waG8yIikpKSAlPiUgCiAgICBjYmluZCguLCBOQSkKICBvdXRfbWF0ID0gaWZlbHNlKGlzLm5hKHVwcGVyKSwgbG93ZXIsIHVwcGVyKQogIHJldHVybihvdXRfbWF0KQp9CmBgYAoKYGBge3J9Cm15X3Bsb3RfdHJpYW5nbGVfaGVhdG1hcCA8LSBmdW5jdGlvbihhbHQsIHZhcil7CiAgIyB0aGlzIGZ1bmN0aW9uIHRha2VzIHRoZSBvdXRwdXQgb2YgdGhlIGZ1bmN0aW9uIGFib3ZlIGFuZCBtYWtlcyBhIGhlYXRtYXAKICAjIHVzaW5nIHBoZWF0bWFwIGZ1bmN0aW9uLCB0aGVuIHJvdGF0ZXMgaXQgdXNpbmcgZ3JpZCBncmFwaGljcwogICMgdGhhbmtzIHRvIGh0dHBzOi8vYm9va2Rvd24ub3JnL3JkcGVuZy9SUHJvZ0RBL3RoZS1ncmlkLXBhY2thZ2UuaHRtbCNncmlkLWdyYXBoaWNzLWNvb3JkaW5hdGUtc3lzdGVtcwogICMgYWRkaW5nIHRpdGxlIGJhc2VkIG9uIGh0dHBzOi8vZGF2ZXRhbmcuZ2l0aHViLmlvL211c2UvcGhlYXRtYXAuaHRtbAogIAogICMgY29uc3RydWN0IHRpdGxlIG9mIHBsb3QKICByZWYgPSBpZmVsc2UoYWx0ID09ICJDIiwgIlNjUGhvNCIsICJDZ1BobzQiKQogIGJnID0gaWZlbHNlKHZhciA9PSAiQV9QSE8yIiwgIndpdGggUEhPMiIsICJ3L28gcGhvMiIpCiAgbXlfdGl0bGUgPC0gcGFzdGUoIkVwaXN0YXNpcyBiZXR3ZWVuIHJlZ2lvbnMgb24iLCByZWYsICJiYWNrZ3JvdW5kIiwgYmcpCiAgdGVzdCA8LSBteV91cHBlcl90cmlhbmd1bGFyX21hdChhbHQgPSBhbHQsIHZhciA9IHZhcikKICBwYWxldHRlTGVuZ3RoID0gNTAKICBteUNvbG9ycyA8LSBjb2xvclJhbXBQYWxldHRlKGMoInN0ZWVsYmx1ZTMiLCAiZ3JheTkwIiwgInJlZCIpKShwYWxldHRlTGVuZ3RoKQogIHJuZyA8LSBtYXgoYWJzKHRlc3QpLCBuYS5ybSA9IFRSVUUpCiAgbXlCcmVha3MgPC0gYyhzZXEoLXJuZywgMCwgbGVuZ3RoLm91dD1jZWlsaW5nKHBhbGV0dGVMZW5ndGgvMikgKyAxKSwgCiAgICAgICAgICAgICAgICBzZXEocm5nL3BhbGV0dGVMZW5ndGgsIHJuZywKICAgICAgICAgICAgICAgICAgICBsZW5ndGgub3V0PWZsb29yKHBhbGV0dGVMZW5ndGgvMikpKQogIHAgPC0gcGhlYXRtYXA6OnBoZWF0bWFwKHRlc3QsIGNvbG9yID0gbXlDb2xvcnMsIGJyZWFrcyA9IG15QnJlYWtzLAogICAgICAgICAgICAgICAgICAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLCBuYV9jb2wgPSBOQSwgc2lsZW50ID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHMgPSBGQUxTRSwgY2x1c3Rlcl9yb3dzID0gRkFMU0UpCiAgdnAgPC0gdmlld3BvcnQoeCA9IDAuNSwgeSA9IDAuMjUsCiAgICAgICAgICAgICAgICAgd2lkdGggPSB1bml0KDQuNSwgImluIiksIGhlaWdodCA9IHVuaXQoNC41LCAiaW4iKSwgYW5nbGUgPSA0NykgCiAgZ3JpZC5uZXdwYWdlKCkKICBwdXNoVmlld3BvcnQodnApCiAgZ3JpZC5kcmF3KHAkZ3RhYmxlKQogIHBvcFZpZXdwb3J0KCkKICBncmlkLnRleHQobGFiZWwgPSBteV90aXRsZSwgeCA9IDAuNSwgeSA9IDAuOTUsIGdwID0gZ3Bhcihmb250c2l6ZSA9IDE2LCBmb250ZmFjZSA9ICJib2xkIikpCiAgcmV0dXJuKHApCn0KYGBgCgpgYGB7cn0KbXlfcGxvdF9jb21iaW5lZF90cmlhbmdsZV9oZWF0bWFwIDwtIGZ1bmN0aW9uKGFsdCl7CiAgIyB0aGlzIGZ1bmN0aW9uIHRha2VzIHRoZSBvdXRwdXQgb2YgdGhlIGZ1bmN0aW9uIG15X2NvbWJpbmVkX3RyaWFuZ3VsYXJfbWF0KCkKICAjIHVzaW5nIHBoZWF0bWFwIGZ1bmN0aW9uLCB0aGVuIHJvdGF0ZXMgaXQgdXNpbmcgZ3JpZCBncmFwaGljcwogICMgdGhhbmtzIHRvIGh0dHBzOi8vYm9va2Rvd24ub3JnL3JkcGVuZy9SUHJvZ0RBL3RoZS1ncmlkLXBhY2thZ2UuaHRtbCNncmlkLWdyYXBoaWNzLWNvb3JkaW5hdGUtc3lzdGVtcwogICMgYWRkaW5nIHRpdGxlIGJhc2VkIG9uIGh0dHBzOi8vZGF2ZXRhbmcuZ2l0aHViLmlvL211c2UvcGhlYXRtYXAuaHRtbAogIAogICMgY29uc3RydWN0IHRpdGxlIG9mIHBsb3QKICByZWYgPSBpZmVsc2UoYWx0ID09ICJDIiwgIlNjUGhvNCIsICJDZ1BobzQiKQogIG15X3RpdGxlIDwtIHBhc3RlKCJFcGlzdGFzaXMgYmV0d2VlbiByZWdpb25zIG9uIiwgcmVmLCAiYmFja2dyb3VuZCIpCiAgdGVzdCA8LSBteV9jb21iaW5lZF90cmlhbmd1bGFyX21hdChhbHQgPSBhbHQpCiAgcGFsZXR0ZUxlbmd0aCA9IDUwCiAgbXlDb2xvcnMgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCJzdGVlbGJsdWUiLCAiZ3JheTkwIiwgInJlZCIpKShwYWxldHRlTGVuZ3RoKQogIHJuZyA8LSBtYXgoYWJzKHRlc3QpLCBuYS5ybSA9IFRSVUUpCiAgbXlCcmVha3MgPC0gYyhzZXEoLXJuZywgMCwgbGVuZ3RoLm91dD1jZWlsaW5nKHBhbGV0dGVMZW5ndGgvMikgKyAxKSwgCiAgICAgICAgICAgICAgICBzZXEocm5nL3BhbGV0dGVMZW5ndGgsIHJuZywKICAgICAgICAgICAgICAgICAgICBsZW5ndGgub3V0PWZsb29yKHBhbGV0dGVMZW5ndGgvMikpKQogIHAgPC0gcGhlYXRtYXA6OnBoZWF0bWFwKHRlc3QsIGNvbG9yID0gbXlDb2xvcnMsIGJyZWFrcyA9IG15QnJlYWtzLAogICAgICAgICAgICAgICAgICAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLCBuYV9jb2wgPSBOQSwgc2lsZW50ID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHMgPSBGQUxTRSwgY2x1c3Rlcl9yb3dzID0gRkFMU0UpCiAgdnAgPC0gdmlld3BvcnQoeCA9IDAuNSwgeSA9IDAuNDUsCiAgICAgICAgICAgICAgICAgd2lkdGggPSB1bml0KDMsICJpbiIpLCBoZWlnaHQgPSB1bml0KDIuOCwgImluIiksIGFuZ2xlID0gNDcpIAogIGdyaWQubmV3cGFnZSgpCiAgcHVzaFZpZXdwb3J0KHZwKQogIGdyaWQuZHJhdyhwJGd0YWJsZSkKICBwb3BWaWV3cG9ydCgpCiAgZ3JpZC50ZXh0KGxhYmVsID0gbXlfdGl0bGUsIHggPSAwLjUsIHkgPSAwLjk1LCAKICAgICAgICAgICAgZ3AgPSBncGFyKGZvbnRzaXplID0gMTYsIGZvbnRmYWNlID0gImJvbGQiKSkKICBncmlkLnRleHQobGFiZWwgPSAiV2l0aCBQaG8yIiwgeCA9IDAuMSwgeSA9IDAuNjUsIGp1c3QgPSBjKCJsZWZ0IiwgInRvcCIpLAogICAgICAgICAgICBncCA9IGdwYXIoZm9udHNpemUgPSAxNCwgZm9udGZhY2UgPSAiYm9sZCIpKQogIGdyaWQudGV4dChsYWJlbCA9ICJXaXRob3V0IHBobzIiLCB4ID0gMC4xLCB5ID0gMC4yNSwganVzdCA9IGMoImxlZnQiLCAidG9wIiksIAogICAgICAgICAgICBncCA9IGdwYXIoZm9udHNpemUgPSAxNCwgZm9udGZhY2UgPSAiYm9sZCIpKQogIHJldHVybihwKQp9CmBgYAoKYGBge3J9CnBuZygiLi4vaW1nLzIwMjQwMTE1LXRyaWFuZ2xlLWhlYXRtYXAtQ2dQaG80LXJlZi5wbmciLCB3aWR0aCA9IDcsIGhlaWdodCA9IDUsIHVuaXRzID0gImluIiwgcmVzID0gMzAwKQpwMSA8LSBteV9wbG90X2NvbWJpbmVkX3RyaWFuZ2xlX2hlYXRtYXAoIkMiKQpkZXYub2ZmKCkKcG5nKCIuLi9pbWcvMjAyNDAxMTUtdHJpYW5nbGUtaGVhdG1hcC1TY1BobzQtcmVmLnBuZyIsIHdpZHRoID0gNywgaGVpZ2h0ID0gNSwgdW5pdHMgPSAiaW4iLCByZXMgPSAzMDApCnAyIDwtIG15X3Bsb3RfY29tYmluZWRfdHJpYW5nbGVfaGVhdG1hcCgiUyIpCmRldi5vZmYoKQpgYGAK